提取美国邮政编码的正则表达式,但不包括伪编码。

5
使用XML包和XPath从网站上抓取地址时,有时我只能得到一个包含所需邮政编码的字符串。提取邮政编码很简单,但有时会出现其他五位数的字符串。以下是在数据框中出现该问题的一些变体。
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位数字字符串。
从地址字符串中提取邮政编码
\b\d{5}\b(?!.*\b\d{5}\b)

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

我的问题:什么R代码可以找到真正的邮政编码并忽略假冒伪劣的?


@CarlWitthoft:感谢您提供的一篇文章,再次强调了解析HTML和在HTML上使用正则表达式之间的区别。我明白了。我的问题是,当解析完成并返回一个包含许多地址组件的块字符串时,该怎么办?难道不是使用正则表达式工具吗? - lawyeR
1
律师,请忽略那个评论,它与您的问题无关。这只是一些人添加毫无意义的评论,因为他们认为这很有趣。针对您的问题[从字符串中返回最后一组连续的5个数字],使用正则表达式没有任何问题。(话虽如此,如果您的问题比这更复杂,可能不适用于正则表达式。) - Joe
2个回答

4
这是我的第一个正则表达式答案(我还在学习),所以希望我不会说错话导致你走错方向。
基本上,这个正则表达式查找的是最后一个看起来像邮政编码的字符串,该字符串不跟随另一个看起来像邮政编码的字符串。
基本语法是pattern(?!.*pattern),它表示仅在没有后续匹配任何.*pattern(负向前瞻断言语法:(?! ))的情况下匹配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"

1

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 的评论:

我认为您需要一些比 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")
)

提取函数,即使在邮政编码后面也有字符

## 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。是否有解决方法? - lawyeR
不确定我之前的评论中是否有正确的@。请告诉我。此外,是否有参数可以关闭前面的连字符和最后四位数字?例如,使用02138而不是02138-1234。 - lawyeR
@lawyeR 这超出了 qdapRegex 的一致性和验证范围,但您可以使用 rm_ 来构建自己的函数来执行此操作,就像我在上面的编辑中演示的那样。 - Tyler Rinker
非常感谢,印象深刻。谢谢。 - lawyeR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接