正则表达式提取美国邮政编码,但不提取人工码

时间:2014-08-07 10:57:59

标签: regex r string

使用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位数字符串。&#34;
Extracting a zip code from an address string

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

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

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

2 个答案:

答案 0 :(得分:3)

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

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

基本语法是pattern(?!.*pattern),只有在没有遵循{{1>}的情况下才会匹配{一个负面的前瞻断言,语法:pattern任何东西 (?! ) .*

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

pattern

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

将所有内容与?一起搜索匹配,(-[0-9]{4})为我解释结果,我得到:

gregexpr

答案 1 :(得分: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"

编辑 Per @ 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