r不会对从地址中提取英国邮政编码的正则表达式进行排序

Amy*_*y M 3 regex r match postal-code order-of-execution

我试图R中从地址字符串中提取英国的邮政编码,使用由英国政府提供的正则表达式在这里.

这是我的功能:

address_to_postcode <- function(addresses) {

  # 1. Convert addresses to upper case
  addresses = toupper(addresses)

  # 2. Regular expression for UK postcodes:
  pcd_regex = "[Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) {0,1}[0-9][A-Za-z]{2})"

  # 3. Check if a postcode is present in each address or not (return TRUE if present, else FALSE)
  present <- grepl(pcd_regex, addresses)

  # 4. Extract postcodes matching the regular expression for a valid UK postcode
  postcodes <- regmatches(addresses, regexpr(pcd_regex, addresses))

  # 5. Return NA where an address does not contain a (valid format) UK postcode
  postcodes_out <- list()
  postcodes_out[present] <- postcodes
  postcodes_out[!present] <- NA

  # 6. Return the results in a vector (should be same length as input vector)
  return(do.call(c, postcodes_out))
}
Run Code Online (Sandbox Code Playgroud)

根据指导文件,这个正则表达式寻找的逻辑如下:

"GIR 0AA"或一个字母后跟一个或两个数字或一个字母后跟第二个字母,必须是ABCDEFGHJ KLMNOPQRSTUVWXY之一(即不是我),然后是一个或两个数字或一个字母后跟一个数字,然后是另一个字母或两部分邮政编码,其中第一部分必须是一个字母后跟第二个字母,必须是ABCDEFGH JKLMNOPQRSTUVWXY之一(即不是我),然后是一个数字,之后可选择另一个字母AND第二部分(由第一部分的空格分隔)必须是一个数字后跟两个字母.允许使用大写和小写字符的组合.注意:长度由正则表达式确定,介于2到8个字符之间.

我的问题是,当使用没有^$锚点的正则表达式时,这个逻辑并没有完全保留(因为我必须在这种情况下做,因为邮政编码可能在地址字符串中的任何地方); 我正在努力的是如何在部分(而不是完整)字符串匹配中保留每个段的顺序和字符数.

请考虑以下示例:

> address_to_postcode("1A noplace road, random city, NR1 2PK, UK")
[1] "NR1 2PK"
Run Code Online (Sandbox Code Playgroud)

根据指南中的逻辑,邮政编码中的第二个字母不能是'z'(并且还有一些其他排除); 但是看看当我添加'z'时会发生什么:

> address_to_postcode("1A noplace road, random city, NZ1 2PK, UK")
[1] "Z1 2PK"
Run Code Online (Sandbox Code Playgroud)

...在这种情况下,我希望输出是NA.

添加锚点(针对不同的用例)似乎没有帮助,因为即使它位于错误的位置,仍然可以接受'z':

> grepl("^[Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) {0,1}[0-9][A-Za-z]{2})$", "NZ1 2PK")
[1] TRUE
Run Code Online (Sandbox Code Playgroud)

两个问题:

  1. 我是否误解了正则表达式的逻辑和
  2. 如果没有,我该如何纠正它(即为什么指定的字母和字符范围不是它们在正则表达式中的位置所独有的)?

ctw*_*els 7

编辑

自从发布这个答案以来,我深入研究了英国政府的正则表达,发现了更多的问题.我在这里发布了另一个答案,它描述了所有问题并提供了格式不正确的正则表达式的替代方案.


注意

请注意,我在这里发布原始正则表达式.\移植到时,您需要转义某些字符(如反斜杠).


问题

这里有很多问题,所有问题都是由创建正在检索正则表达式的文档的人或创建它的编码人员造成的.

1.空间角色

我的猜测是,当你从你提供的链接复制正则表达式时,它将空格字符转换为换行符并删除它(这正是我最初所做的).相反,您需要将其更改为空格字符.

^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
                                                                                                                                                here ^
Run Code Online (Sandbox Code Playgroud)

2.边界

您需要删除锚点^,$因为它们表示行的开始和结束.相反,请将正则表达式包装在其中,(?:)\b在两端放置(字边界),如下所示.事实上,文档中的正则表达式是不正确的(请参阅侧面说明以获取更多信息),因为它无法正确锚定模式.

请参阅此处使用的正则表达式

\b(?:([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2}))\b
^^^^^                                                                                                                                                                      ^^^
Run Code Online (Sandbox Code Playgroud)

3.品格类疏忽

有一个丢失-的字符类如指出@deadcrab在他的答案在这里.

\b(?:([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2}))\b
                                                                                           ^
Run Code Online (Sandbox Code Playgroud)

他们选择了错误的角色类!

在文档中,它明确指出:

两部分邮政编码,第一部分必须是:

  • 一个字母后跟第二个字母,必须是ABCDEFGHJKLMNOPQRSTUVWXY(ie.not I)之一,然后是一个数字,之后可选择另外一个字母

他们使错误的角色类可选!

\b(?:([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2}))\b
                                                                                                                                        ^^^^^^
                                                                                                                        it should be this one ^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

整件事情太糟糕了......

这个正则表达式有很多问题我决定重写它.它可以很容易地简化,以执行当前为匹配文本所需的一小部分步骤.

\b(?:[A-Za-z][A-HJ-Ya-hj-y]?[0-9][0-9A-Za-z]? [0-9][A-Za-z]{2}|[Gg][Ii][Rr] 0[Aa]{2})\b
Run Code Online (Sandbox Code Playgroud)

回答

正如我在下面的评论中所提到的,一些邮政编码缺少空格字符.对于邮政编码中缺少的空格(例如NR12PK),只需?在空格之后添加一个空格,如下面的正则表达式所示:

\b(?:[A-Za-z][A-HJ-Ya-hj-y]?[0-9][0-9A-Za-z]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})\b
                                             ^^                             ^^
Run Code Online (Sandbox Code Playgroud)

您还可以使用以下内容缩短上面的正则表达式并使用不区分大小写的标志(ignore.case(pattern)或者ignore_case = TRUE,具体取决于使用的方法.):

\b(?:[A-Z][A-HJ-Y]?[0-9][0-9A-Z]? ?[0-9][A-Z]{2}|GIR ?0A{2})\b
Run Code Online (Sandbox Code Playgroud)

注意

请注意,正则表达式仅验证字符串的可能格式,并且无法实际识别邮政编码是否合法存在.为此,您应该使用API​​.还有一些边缘情况,此正则表达式无法正确匹配有效的邮政编码.有关这些邮政编码的列表,请参阅此维基百科文章.

下面的正则表达式还匹配以下内容(使其不区分大小写以匹配小写变体):

  • 英国海外领土
  • 英国军队邮局
    • 虽然他们最近改变它以与英国邮政编码系统一致BF,然后是一个数字(从开始BF1),它们被认为是可选的替代邮政编码
  • 该文章中概述的特殊情况(以及SAN TA1- 圣诞老人的有效邮政编码!)

在这里看到这个正则表达式.

\b(?:(?:[A-Z][A-HJ-Y]?[0-9][0-9A-Z]?|ASCN|STHL|TDCU|BBND|[BFS]IQ{2}|GX11|PCRN|TKCA) ?[0-9][A-Z]{2}|GIR ?0A{2}|SAN ?TA1|AI-?[0-9]{4}|BFPO[ -]?[0-9]{2,3}|MSR[ -]?1(?:1[12]|[23][135])0|VG[ -]?11[1-6]0|[A-Z]{2} ? [0-9]{2}|KY[1-3][ -]?[0-2][0-9]{3})\b
Run Code Online (Sandbox Code Playgroud)

我还建议任何实现此答案的人阅读这篇题为"英国邮政编码正则表达式(综合)"的StackOverflow问题.


边注

您链接到的文档(批量数据传输:CAS上载的附加验证 - 第3部分:英国邮政编码正则表达式)实际上有一个不正确编写的正则表达式.

问题部分所述,他们应该:

  1. 将整个表达式包装(?:)并将锚点放在非捕获组周围.他们的正则表达式,因为它的立场,因为看到会失败在某些情况下在这里.
  2. -其中一个字符类中 也缺少正则表达式
  3. 它还使错误的字符类可选.