【问题标题】:Regex to extract US zip codes but not faux codes正则表达式提取美国邮政编码,但不提取虚假代码
【发布时间】:2014-08-07 10:57:59
【问题描述】:

使用 XML 包和 XPath 从网站上抓取地址,有时我只能得到一个字符串,其中嵌入了我想要的邮政编码。提取邮政编码很简单,但有时还会出现其他五位数的字符串。

以下是 df 中问题的一些变体。

zips <- data.frame(id = seq(1, 5), address = c("Company, 18540 Main Ave., City, ST 12345", "Company 18540 Main Ave. City ST 12345-0000", "Company 18540 Main Ave. City State 12345", "Company, 18540 Main Ave., City, ST 12345 USA", "Company, One Main Ave Suite 18540, City, ST 12345")) 

提取邮政编码(5位和加4位)的R语句如下,但它被门牌号和套房号的虚假邮政编码所欺骗(其他地址字符串中可能存在其他可能性) )。

regmatches(zips$address, gregexpr("\\d{5}([-]?\\d{4})?", zips$address, perl = TRUE))

对上一个 SO 问题的回答建议“正则表达式将返回最后一个连续的五位字符串。它使用负前瞻来确保在返回的字符串之后不存在 5 位字符串。”
Extracting a zip code from an address string

\b\d{5}\b(?!.*\b\d{5}\b)

但是那个问题和答案涉及 PHP,并提供了一个带有 preg_matches()` 的 if 循环我不熟悉那些语言和工具,但这个想法可能是正确的。

我的问题:什么 R 代码会找到真正的邮政编码并忽略虚假相似?

【问题讨论】:

  • @CarlWitthoft:感谢您引用一篇文章,再次强调解析 HTML 和在 HTML 上使用正则表达式之间的区别。取点。我的问题是当解析完成所有它可以做的事情时该怎么做,并返回一个包含大量地址组件的块字符串。那么正则表达式是调用的工具,这不是真的吗?
  • 律师,请忽略该评论,它与您的问题无关。只是人们添加毫无意义的 cmets,因为他们认为自己很有趣。鉴于您的问题[从字符串中返回最后一组 5 个连续数字],为此使用正则表达式没有问题。 (也就是说,如果您的问题比这更复杂,那可能不合适。)

标签: regex r string


【解决方案1】:

这是我的第一个正则表达式答案(我还在学习),所以希望我没有说错任何话来引导你走错方向。

基本上,正如您在问题中暗示的那样,此正则表达式会查找最后一个看起来像邮政编码的字符串,后面没有看起来像邮政编码的字符串

基本语法是pattern(?!.*pattern),它表示只有在 anything .* 没有遵循它(否定的前瞻断言,语法:(?! ))时才匹配em>和 pattern

所以我们可以用您感兴趣的内容替换模式:

[0-9]{5}(-[0-9]{4})?

即,正好为 5 个字符 {5} 的数字字符串 [0-9](可以选择在 ? 后面跟着另一个定义为连字符的组和另一个长度为 4 的数字字符串 (-[0-9]{4})

将它们与gregexpr 一起搜索匹配项和regmatches 为我解释结果,我得到:

zips <- data.frame(id = seq(1, 5), address = c("Company, 18540 Main Ave., City, ST 12345", "Company 18540 Main Ave. City ST 12345-0000", "Company 18540 Main Ave. City State 12345", "Company, 18540 Main Ave., City, ST 12345 USA", "Company, One Main Ave Suite 18540, City, ST 12345")) 
regmatches(zips$address,
           gregexpr('[0-9]{5}(-[0-9]{4})?(?!.*[0-9]{5}(-[0-9]{4})?)', zips$address, perl = TRUE))

# [[1]]
# [1] "12345"
# 
# [[2]]
# [1] "12345-0000"
# 
# [[3]]
# [1] "12345"
# 
# [[4]]
# [1] "12345"
# 
# [[5]]
# [1] "12345"

【讨论】:

    【解决方案2】:

    qdapRegex 包有 rm_zip 函数:

    zips <- data.frame(id = seq(1, 5), 
        address = c("Company, 18540 Main Ave., City, ST 12345", 
        "Company 18540 Main Ave. City ST 12345-0000", 
        "Company 18540 Main Ave. City State 12345", 
        "Company, 18540 Main Ave., City, ST 12345 USA", 
        "Company, One Main Ave Suite 18540, City, ST 12345")
    )
    
    lapply(rm_zip(zips$address, extract=TRUE), tail, 1)
    
    ## [[1]]
    ## [1] "12345"
    ## 
    ## [[2]]
    ## [1] "12345-0000"
    ## 
    ## [[3]]
    ## [1] "12345"
    ## 
    ## [[4]]
    ## [1] "12345"
    ## 
    ## [[5]]
    ## [1] "12345"
    

    编辑根据@lawyeR 的cmets:

    我认为您需要一些比qdapRegex 使用的字典系统更具体的正则表达式。 rm_zip 的当前实现允许验证目的,因此我不会更改它使用的正则表达式以使其更灵活。我也不会更改函数 rm_zip 以拥有额外的参数/参数,因为 qdapRegex 试图拥有一致的操作函数。

    话虽如此,您可以使用rm_ 函数创建自己的函数并提供您自己的正则表达式。我已经使用您评论中指定的两个参数完成了此操作:

    更复杂的数据集:

    zips <- data.frame(id = seq(1, 6), 
        address = c("Company, 18540 Main Ave., City, ST 12345", 
        "Company 18540 Main Ave. City ST 12345-0000", 
        "Company 18540 Main Ave. City State 12345", 
        "Company, 18540 Main Ave., City, ST 12345 USA", 
        "Company, One Main Ave Suite 18540m, City, ST 12345",
        "company 12345678")
    )
    

    即使角色跟随 zip 也可以抓取的功能

    ## paste together a more flexible regular expression    
    pat <- pastex(
        "@rm_zip", 
        "(?<!\\d)\\d{5}(?!\\d)",
        "(?<!\\d)\\d{5}-\\d{4}(?!\\d)"
    )
    ## Create your own function that extract is set to TRUE
    rm_zip2 <- rm_(pattern=pat, extract=TRUE)
    rm_zip2(zips$address)
    
    ## [[1]]
    ## [1] "18540" "12345"
    ## 
    ## [[2]]
    ## [1] "18540"      "12345-0000"
    ## 
    ## [[3]]
    ## [1] "18540" "12345"
    ## 
    ## [[4]]
    ## [1] "18540" "12345"
    ## 
    ## [[5]]
    ## [1] "18540" "12345"
    ## 
    ## [[6]]
    ## [1] NA
    

    仅提取 5 位数拉链的功能

    rm_zip3 <- rm_(pattern="(?<!\\d)\\d{5}(?!\\d)", extract=TRUE)
    rm_zip3(zips$address)
    
    ## [[1]]
    ## [1] "18540" "12345"
    ## 
    ## [[2]]
    ## [1] "18540" "12345"
    ## 
    ## [[3]]
    ## [1] "18540" "12345"
    ## 
    ## [[4]]
    ## [1] "18540" "12345"
    ## 
    ## [[5]]
    ## [1] "18540" "12345"
    ## 
    ## [[6]]
    ## [1] NA
    

    【讨论】:

    • 谢谢。我将 rm_zip() 与我一直在使用的调用进行了比较: str_extract(string = locations$location, pattern = "\\d{5}") 在我拥有的大量数据上。您的 qdapRegex 函数没有提取 7 个邮政编码,每个邮政编码后面都有一个字母,例如 02138M。有解决办法吗?
    • 不确定我之前的评论有正确的@。请告诉我。另外,是否有一个参数可以关闭最后四位数字和前面的连字符?例如,02138 而不是 02138-1234
    • @lawyeR 这超出了qdapRegex 的一致性和验证范围,但您可以使用rm_ 构建自己的函数来执行此操作,正如我在上面的编辑中演示的那样。跨度>
    • 非常感激,并且印象深刻。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多