我在一个正则表达式后,将验证一个完整的复杂的英国邮政编码只在输入字符串。所有不常见的邮政编码形式必须包括以及通常。例如:

匹配

CW3 9不锈钢 SE5 0EG SE50EG Se5 0eg WC2H 7LT

不匹配

aWC2H 7LT WC2H 7LTa WC2H

我怎么解决这个问题?


当前回答

我看了一下上面的一些答案,我不建议使用@Dan的答案(12月15日至10日)中的模式,因为它错误地将近0.4%的有效邮政编码标记为无效,而其他的则没有。

军械测量所提供的代号开放服务包括:

包含英国所有当前邮政编码单位的列表

我使用grep从这些数据中针对完整的邮政编码列表(7月6日至13日)运行了上面的每个正则表达式:

cat CSV/*.csv |
    # Strip leading quotes
    sed -e 's/^"//g' |
    # Strip trailing quote and everything after it
    sed -e 's/".*//g' |
    # Strip any spaces
    sed -E -e 's/ +//g' |
    # Find any lines that do not match the expression
    grep --invert-match --perl-regexp "$pattern"

邮政编码共有1,686,202个。

以下是与每个$模式不匹配的有效邮政编码的数量:

'^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]?[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$'
# => 6016 (0.36%)
'^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW]) ?[0-9][ABD-HJLNP-UW-Z]{2})$'
# => 0
'^GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|BX|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}$'
# => 0

当然,这些结果只处理被错误地标记为无效的有效邮政编码。所以:

'^.*$'
# => 0

在过滤无效邮编方面,我并没有说哪种模式是最好的。

其他回答

我最近发表了一个关于英国R语言邮政编码的答案。我发现英国政府的正则表达式模式是不正确的,不能正确地验证一些邮政编码。不幸的是,这里的许多答案都是基于这个错误的模式。

我将在下面概述其中的一些问题,并提供一个实际工作的修改后的正则表达式。


Note

我的回答(以及一般的正则表达式):

只验证邮政编码格式。 不能确保邮政编码合法存在。 为此,使用适当的API!更多信息请看本的回答。


如果你不关心糟糕的正则表达式,只想跳到答案,向下滚动到答案部分。

糟糕的正则表达式

不应使用本节中的正则表达式。

这是英国政府提供给开发者的失败正则表达式(不确定这个链接会持续多久,但你可以在他们的批量数据传输文档中看到它):

^([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})$

问题

问题 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})$

大多数开发人员会做的第一件事就是毫不犹豫地删除换行符。现在正则表达式将不匹配带有空格的邮政编码(GIR 0AA邮政编码除外)。

要解决这个问题,换行符应该替换为空格字符:

^([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})$
                                                                                                                                                     ^

问题2 -边界

在这里查看正则表达式的使用。

^([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})$
^^                     ^ ^                                                                                                                                            ^^

邮政编码正则表达式不正确地锚定正则表达式。如果fooA11 1AA这样的值通过,任何使用这个正则表达式验证邮政编码的人都可能会感到惊讶。这是因为它们锚定了第一个选项的开始和第二个选项的结束(彼此独立),正如上面的正则表达式所指出的那样。

这意味着^(断言在行开始位置)只对第一个选项([Gg][Ii][Rr] 0[Aa]{2})有效,因此第二个选项将验证以邮政编码结尾的任何字符串(不管前面是什么)。

类似地,第一个选项没有锚定到行$的末尾,所以GIR 0AAfoo也被接受。

^([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})$

为了解决这个问题,两个选项都应该被包装在另一个组(或非捕获组)中,并在其周围放置锚点:

^(([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}))$
^^                                                                                                                                                                      ^^

问题3 -不正确的字符集

在这里查看正则表达式的使用。

^([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})$
                                                                                       ^^

正则表达式在这里缺少一个-来指示字符范围。按照目前的情况,如果邮政编码的格式是ANA NAA(其中a代表字母,N代表数字),并且它的开头不是a或Z,那么它将失败。

这意味着它将匹配A1A 1AA和Z1A 1AA,但不匹配B1A 1AA。

为了解决这个问题,字符-应该放在A和Z之间,在各自的字符集:

^([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})$
                                                                                        ^

问题4 -错误的可选字符集

在这里查看正则表达式的使用。

^([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})$
                                                                                                                                        ^

我发誓他们在网上公布之前都没测试过。他们设置了错误的可选字符集。他们在选项2(组9)的第四个子选项中设置了[0-9]选项。这允许正则表达式匹配格式不正确的邮政编码,如AAA 1AA。

为了解决这个问题,将下一个字符类改为可选的(随后使集合[0-9]精确匹配一次):

^([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})$
                                                                                                                                                ^

问题5 -性能

这个正则表达式的性能非常差。首先,他们在开始时放置了最不可能匹配GIR 0AA的模式选项。有多少用户可能使用这个邮政编码而不是其他邮政编码;可能从来没有?这意味着每次使用正则表达式时,它必须先用完这个选项,然后再进行下一个选项。要了解性能如何受到影响,请检查在翻转选项(22)后,原始regex执行的步数(35)相对于相同的regex。

性能的第二个问题是由于整个正则表达式的结构方式。如果一个选项失败了,那么回溯每个选项就没有意义了。当前正则表达式的结构方式可以大大简化。我在答案部分提供了一个修复方法。

问题6:空格

在这里查看正则表达式的使用

这本身可能不被认为是一个问题,但它确实引起了大多数开发人员的关注。正则表达式中的空格不是可选的,这意味着输入邮政编码的用户必须在邮政编码中放置空格。这是一个简单的解决方案,只需添加?空格后显示为可选。有关修复方法,请参阅答案部分。


回答

1. 修复英国政府的正则表达式

修复问题一节中概述的所有问题并简化模式,可以得到以下更短、更简洁的模式。我们还可以删除大多数组,因为我们是将邮编作为一个整体(而不是单个部分)进行验证:

在这里查看正则表达式的使用

^([A-Za-z][A-Ha-hJ-Yj-y]?[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})$

可以通过删除一个大小写(大写或小写)中的所有范围并使用不区分大小写的标志来进一步缩短这个时间。注意:有些语言没有,所以请使用上面的长一点的。每种语言都以不同的方式实现不区分大小写标志。

在这里查看正则表达式的使用。

^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$

再次用\d替换[0-9](如果你的regex引擎支持它):

在这里查看正则表达式的使用。

^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$

2. 简化的模式

在不确保特定的字母字符的情况下,可以使用以下方法(请记住从1。修复英国政府的正则表达式也被应用在这里):

在这里查看正则表达式的使用。

^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$

甚至如果你不关心特殊情况GIR 0AA:

^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$

3.复杂的模式

我不建议过度核实邮编,因为任何时候都可能出现新的地区、地区和街道。我建议做的是,增加对边缘情况的支持。存在一些特殊情况,并在维基百科的这篇文章中概述。

下面是包含3的子节的复杂正则表达式。(3.1, 3.2, 3.3)。

与1中的模式相关。修复英国政府的正则表达式:

在这里查看正则表达式的使用

^(([A-Z][A-HJ-Y]?\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$

和2的关系。简化的模式:

在这里查看正则表达式的使用

^(([A-Z]{1,2}\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$

3.1英国海外领土

维基百科的文章目前陈述(一些格式略微简化):

AI-1111: Anguila ASCN 1ZZ: Ascension Island STHL 1ZZ: Saint Helena TDCU 1ZZ: Tristan da Cunha BBND 1ZZ: British Indian Ocean Territory BIQQ 1ZZ: British Antarctic Territory FIQQ 1ZZ: Falkland Islands GX11 1ZZ: Gibraltar PCRN 1ZZ: Pitcairn Islands SIQQ 1ZZ: South Georgia and the South Sandwich Islands TKCA 1ZZ: Turks and Caicos Islands BFPO 11: Akrotiri and Dhekelia ZZ 11 & GE CX: Bermuda (according to this document) KY1-1111: Cayman Islands (according to this document) VG1111: British Virgin Islands (according to this document) MSR 1111: Montserrat (according to this document)

一个只匹配英国海外领土的包罗万象的正则表达式可能是这样的:

在这里查看正则表达式的使用。

^((ASCN|STHL|TDCU|BBND|[BFS]IQQ|GX\d{2}|PCRN|TKCA) ?\d[A-Z]{2}|(KY\d|MSR|VG|AI)[ -]?\d{4}|(BFPO|[A-Z]{2}) ?\d{2}|GE ?CX)$

3.2英国军队邮局

虽然他们最近已经将其更改为更好地与英国邮政编码系统保持一致的bf#(其中#代表一个数字),但它们被认为是可选的替代邮政编码。这些邮政编码遵循BFPO的格式,后面跟着1-4位数字:

在这里查看正则表达式的使用

^BFPO ?\d{1,4}$

3.3圣诞老人?

还有一个关于圣诞老人的特殊情况(在其他答案中提到过):SAN TA1是有效的邮政编码。一个正则表达式非常简单:

^SAN ?TA1$

我建议你看看英国政府的邮政编码数据标准[链接现在死了;XML的存档,参见维基百科的讨论]。这里有关于数据的简要描述,附带的xml模式提供了一个正则表达式。这可能不是你想要的,但会是一个很好的起点。RegEx与XML略有不同,因为给定的定义允许在格式A9A 9AA中第三个位置的P字符。

英国政府提供的正则表达式为:

([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]?))))\s?[0-9][A-Za-z]{2})

正如维基百科讨论中指出的那样,这将允许一些非真实的邮政编码(例如以AA, ZY开头的邮政编码),并且它们确实提供了一个更严格的测试,您可以尝试一下。

这是谷歌在i18napis.appspot.com域名上的正则表达式:

GIR[ ]?0AA|((AB|AL|B|BA|BB|BD|BH|BL|BN|BR|BS|BT|BX|CA|CB|CF|CH|CM|CO|CR|CT|CV|CW|DA|DD|DE|DG|DH|DL|DN|DT|DY|E|EC|EH|EN|EX|FK|FY|G|GL|GY|GU|HA|HD|HG|HP|HR|HS|HU|HX|IG|IM|IP|IV|JE|KA|KT|KW|KY|L|LA|LD|LE|LL|LN|LS|LU|M|ME|MK|ML|N|NE|NG|NN|NP|NR|NW|OL|OX|PA|PE|PH|PL|PO|PR|RG|RH|RM|S|SA|SE|SG|SK|SL|SM|SN|SO|SP|SR|SS|ST|SW|SY|TA|TD|TF|TN|TQ|TR|TS|TW|UB|W|WA|WC|WD|WF|WN|WR|WS|WV|YO|ZE)(\d[\dA-Z]?[ ]?\d[ABD-HJLN-UW-Z]{2}))|BFPO[ ]?\d{1,4}

看看本页的python代码:

http://www.brunningonline.net/simon/blog/archives/001292.html

I've got some postcode parsing to do. The requirement is pretty simple; I have to parse a postcode into an outcode and (optional) incode. The good new is that I don't have to perform any validation - I just have to chop up what I've been provided with in a vaguely intelligent manner. I can't assume much about my import in terms of formatting, i.e. case and embedded spaces. But this isn't the bad news; the bad news is that I have to do it all in RPG. :-( Nevertheless, I threw a little Python function together to clarify my thinking.

我用它来处理邮政编码。

看起来我们将使用^(GIR ?0AA|[a - pr - uwyz]([0-9]{1,2}|([a - hk - y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][a - hjkps - uw])? [0-9][ABD-HJLNP-UW-Z]{2})$,这是上面Minglis建议的略有修改的版本。

然而,我们将不得不调查到底是什么规则,因为上面列出的各种解决方案似乎适用于不同的规则,哪些字母是允许的。

经过一番研究,我们找到了更多的信息。显然,“govtalk.gov.uk”上的一个页面会指向邮政编码规范govtalk-postcodes。它指向XML schema中的一个XML模式,该模式提供了邮政编码规则的“伪正则表达式”语句。

我们用它做了一些修改,得到了下面的表达式:

^((GIR &0AA)|((([A-PR-UWYZ][A-HK-Y]?[0-9][0-9]?)|(([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRV-Y]))) &[0-9][ABD-HJLNP-UW-Z]{2}))$

这使得空格是可选的,但限制您只能使用一个空格(将'&'替换为'{0,}表示无限空格)。它假定所有文本都必须是大写的。

如果你想要允许小写,任意数量的空格,使用:

^(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$

这并不包括海外领土,只是强制执行格式,而不是不同地区的存在。它基于以下规则:

可接受以下格式:

“秋天” A9 9 zz A99 9 zz AB9 9 zz AB99 9 zz A9C 9 zz AD9E 9 zz

地点:

9可以是任何一位数。 A可以是除Q、V或X之外的任何字母。 B可以是除I、J或Z之外的任何字母。 C可以是除I、L、M、N、O、P、Q、R、V、X、Y或Z之外的任何字母。 D可以是除I、J或Z之外的任何字母。 E可以是A, B, E, H, M, N, P, R, V, W, X或Y中的任意一个。 Z可以是C、I、K、M、O或V之外的任何字母。

最好的祝愿

科林