从字符串中删除美国邮政编码:R正则表达式

时间:2014-08-09 22:40:13

标签: regex r

我正在尝试从字符串中删除/提取邮政编码。逻辑是我抓住的东西:

  1. 必须包含5个连续数字或
  2. 必须包含5个连续数字后跟一个破折号,然后恰好包含4个连续数字或
  3. 必须包含5个连续数字后跟一个空格,然后恰好包含4个连续数字
  4. 字符串的zip部分可以以空格开头,但可能不是。

    这是一个MWE和我尝试过的。 2个尝试的正则表达式基于this questionthis question

    text.var <- c("Mr. Bean bought 2 tickets 2-613-213-4567",
      "43 Butter Rd, Brossard QC K0A 3P0 – 613 213 4567", 
      "Rat Race, XX, 12345",
      "Ignore phone numbers(613)2134567",
      "Grab zips with dashes 12345-6789 or no space before12345-6789",  
      "Grab zips with spaces 12345 6789 or no space before12345 6789",
      "I like 1234567 dogs"
    )
    
    pattern1 <- "\\d{5}([- ]*\\d{4})?"
    pattern2 <- "[0-9]{5}(-[0-9]{4})?(?!.*[0-9]{5}(-[0-9]{4})?)"
    
    
    regmatches(text.var, gregexpr(pattern1, text.var, perl = TRUE)) 
    regmatches(text.var, gregexpr(pattern2, text.var, perl = TRUE)) 
    
    ## [[1]]
    ## character(0)
    ## 
    ## [[2]]
    ## character(0)
    ## 
    ## [[3]]
    ## [1] "12345"
    ## 
    ## [[4]]
    ## [1] "21345"
    ## 
    ## [[5]]
    ## [1] "12345-6789"
    ## 
    ## [[6]]
    ## [1] "12345"
    ## 
    ## [[7]]
    ## [1] "12345"
    

    所需输出

    ## [[1]]
    ## character(0)
    ## 
    ## [[2]]
    ## character(0)
    ## 
    ## [[3]]
    ## [1] "12345"
    ## 
    ## [[4]]
    ## character(0)
    ## 
    ## [[5]]
    ## [1] "12345-6789" "12345-6789"
    ## 
    ## [[6]]
    ## [1] "12345 6789" "12345 6789"
    ## 
    ## [[7]]
    ## character(0)
    

    注意 R的正则表达式与其他正则表达式类似但是特定于R。这个问题特定于R的正则表达式而不是一般的正则表达式问题。

5 个答案:

答案 0 :(得分:2)

你可以使用这样的正则表达式:

"(?<!\\d)(\\d{5}(?:[-\\s]\\d{4})?)\\b"

<强> Working demo

enter image description here

答案 1 :(得分:2)

这对我有用,并为您的所有示例提供了所需的输出:

"(?<!\\d)(\\d{5}(?:[- ]\\d{4})?)(?!\\d)"

答案 2 :(得分:2)

Lookaround断言

您可以在此处使用Negative Lookbehind和字边界\b的组合。

regmatches(text.var, gregexpr('(?<!\\d)\\d{5}(?:[ -]\\d{4})?\\b', text.var, perl=T))

<强>解释

  • 负面的后视断言,前面的不是数字。
  • Word边界断言,一方面有一个单词字符,另一方面没有。

    (?<!        # look behind to see if there is not:
      \d        #   digits (0-9)
    )           # end of look-behind
    \d{5}       # digits (0-9) (5 times)
    (?:         # group, but do not capture (optional):
      [ -]      #   any character of: ' ', '-'
      \d{4}     #   digits (0-9) (4 times)
    )?          # end of grouping
    \b          # the boundary between a word character (\w) and not a word character
    

其他选项

您可以考虑使用速度更快的stringi库包。

> library(stringi)
> stri_extract_all_regex(text.var, '(?<!\\d)\\d{5}(?:[ -]\\d{4})?\\b')

答案 3 :(得分:1)

RegEx with LookArounds:

(?<![0-9-])([0-9]{5}(?:[ -][0-9]{4})?)(?![0-9-])`  

现场演示:http://regex101.com/r/hU9oK4/1

我们之后的事情:

  • [0-9]{5}重要的部分,正好找5位数字

  • (?:[ -][0-9]{4})?)只有在用空格或减号连接的情况下才可选择再加4次

边界,边界,边界:

  • (?<![0-9-])第一组:负面LookBehind(确保没有数字或短划线)

  • (?![0-9-])最后一组:负面预测( - || - 相同模式......

额外的测试用例:

  

另一个拉链09788-4234后没有空格   98712个
  987122个
  邮编或范围12987-19222?
  这个系列怎么样88101-8892-22912-9991-99101?
  90872-8881

为什么?

  • LookArounds不是consume个字符
  • 你不应该接受误报(例如,较长的编号中的第一个或最后5个数字)
  • ZIP可能在其自己的行上,或者在开头或结尾
  • 您可以碰到无空间地址
  • 以减号开头的5位数不应为邮政编码

最终说明:这不是 最终 或防弹匹配代码,您可能仍会收集一些zip相似内容,尤其是因为您需求中数字组之间的空格

个人注意事项我发现[0-9]个字符类更清晰且更易于理解适用于RegEx的新手,即使他们已包含在一个\d,但它们也更快,并且在RegEx风格之间具有更好的兼容性。另一方面,双重逃避(例如\\d是丑陋的阅读)

答案 4 :(得分:0)

qdapRegex包具有rm_zip功能(基于@ hwnd&#39; s响应):

rm_zip(text.var)
rm_zip(text.var, extract=TRUE)

> rm_zip(text.var, extract=TRUE)
[[1]]
[1] NA

[[2]]
[1] NA

[[3]]
[1] "12345"

[[4]]
[1] NA

[[5]]
[1] "12345-6789" "12345-6789"

[[6]]
[1] "12345 6789" "12345 6789"

[[7]]
[1] NA