我在一个正则表达式后,将验证一个完整的复杂的英国邮政编码只在输入字符串。所有不常见的邮政编码形式必须包括以及通常。例如:
匹配
CW3 9不锈钢 SE5 0EG SE50EG Se5 0eg WC2H 7LT
不匹配
aWC2H 7LT WC2H 7LTa WC2H
我怎么解决这个问题?
我在一个正则表达式后,将验证一个完整的复杂的英国邮政编码只在输入字符串。所有不常见的邮政编码形式必须包括以及通常。例如:
匹配
CW3 9不锈钢 SE5 0EG SE50EG Se5 0eg WC2H 7LT
不匹配
aWC2H 7LT WC2H 7LTa WC2H
我怎么解决这个问题?
当前回答
邮政编码可能会发生变化,验证邮政编码的唯一真正方法是拥有完整的邮政编码列表,并查看它是否存在。
但是正则表达式很有用,因为它们:
是否易于使用和实现 是短暂的 都跑得很快 相当容易维护(与完整的邮政编码列表相比) 仍然捕获大多数输入错误
但是正则表达式往往很难维护,特别是对于那些一开始就没有想到它的人来说。所以它一定是:
尽量简单易懂 相对未来的证明
这意味着这个答案中的大多数正则表达式都不够好。例如,我可以看到[a - pr - uwyz][a - hk - y][0-9][ABEHMNPRV-Y]将匹配形式为AA1A的邮政编码区域-但如果添加了新的邮政编码区域,这将是一个令人头疼的问题,因为很难理解它匹配哪些邮政编码区域。
我还想让我的正则表达式匹配邮政编码的前半部分和后半部分。
所以我想到了这个:
(GIR(?=\s*0AA)|(?:[BEGLMNSW]|[A-Z]{2})[0-9](?:[0-9]|(?<=N1|E1|SE1|SW1|W1|NW1|EC[0-9]|WC[0-9])[A-HJ-NP-Z])?)\s*([0-9][ABD-HJLNP-UW-Z]{2})
在PCRE格式中,可以这样写:
/^
( GIR(?=\s*0AA) # Match the special postcode "GIR 0AA"
|
(?:
[BEGLMNSW] | # There are 8 single-letter postcode areas
[A-Z]{2} # All other postcode areas have two letters
)
[0-9] # There is always at least one number after the postcode area
(?:
[0-9] # And an optional extra number
|
# Only certain postcode areas can have an extra letter after the number
(?<=N1|E1|SE1|SW1|W1|NW1|EC[0-9]|WC[0-9])
[A-HJ-NP-Z] # Possible letters here may change, but [IO] will never be used
)?
)
\s*
([0-9][ABD-HJLNP-UW-Z]{2}) # The last two letters cannot be [CIKMOV]
$/x
对我来说,这是尽可能多地验证之间的正确平衡,与此同时,未来的验证和易于维护。
其他回答
我使用下面的正则表达式,我已经测试了所有有效的英国邮政编码。它基于推荐的规则,但尽可能地精简,并且没有使用任何特殊语言特定的正则表达式规则。
([A-PR-UWYZ]([A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y])?|[0-9]([0-9]|[A-HJKPSTUW])?) ?[0-9][ABD-HJLNP-UW-Z]{2})
它假定邮政编码已转换为大写,并且没有前导字符或尾随字符,但在出码和入码之间接受可选空格。
特殊的“GIR0 0AA”邮政编码被排除在外,并且不会生效,因为它不在官方邮局的邮政编码列表中,据我所知,它不会被用作注册地址。如果需要,作为特殊情况添加它应该是微不足道的。
我最近发表了一个关于英国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$
^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$
Regular expression to match valid UK postcodes. In the UK postal system not all letters are used in all positions (the same with vehicle registration plates) and there are various rules to govern this. This regex takes into account those rules. Details of the rules: First half of postcode Valid formats [A-Z][A-Z][0-9][A-Z] [A-Z][A-Z][0-9][0-9] [A-Z][0-9][0-9] [A-Z][A-Z][0-9] [A-Z][A-Z][A-Z] [A-Z][0-9][A-Z] [A-Z][0-9] Exceptions Position - First. Contraint - QVX not used Position - Second. Contraint - IJZ not used except in GIR 0AA Position - Third. Constraint - AEHMNPRTVXY only used Position - Forth. Contraint - ABEHMNPRVWXY Second half of postcode Valid formats [0-9][A-Z][A-Z] Exceptions Position - Second and Third. Contraint - CIKMOV not used
http://regexlib.com/REDetails.aspx?regexp_id=260
根据维基百科的表格
这种模式适用于所有情况
(?:[A-Za-z]\d ?\d[A-Za-z]{2})|(?:[A-Za-z][A-Za-z\d]\d ?\d[A-Za-z]{2})|(?:[A-Za-z]{2}\d{2} ?\d[A-Za-z]{2})|(?:[A-Za-z]\d[A-Za-z] ?\d[A-Za-z]{2})|(?:[A-Za-z]{2}\d[A-Za-z] ?\d[A-Za-z]{2})
当在Android / Java上使用它时,使用\\d
我们得到了一个说明:
UK postcodes must be in one of the following forms (with one exception, see below): § A9 9AA § A99 9AA § AA9 9AA § AA99 9AA § A9A 9AA § AA9A 9AA where A represents an alphabetic character and 9 represents a numeric character. Additional rules apply to alphabetic characters, as follows: § The character in position 1 may not be Q, V or X § The character in position 2 may not be I, J or Z § The character in position 3 may not be I, L, M, N, O, P, Q, R, V, X, Y or Z § The character in position 4 may not be C, D, F, G, I, J, K, L, O, Q, S, T, U or Z § The characters in the rightmost two positions may not be C, I, K, M, O or V The one exception that does not follow these general rules is the postcode "GIR 0AA", which is a special valid postcode.
我们想出了这个:
/^([A-PR-UWYZ][A-HK-Y0-9](?:[A-HJKS-UW0-9][ABEHMNPRV-Y0-9]?)?\s*[0-9][ABD-HJLNP-UW-Z]{2}|GIR\s*0AA)$/i
但是注意-这允许组之间有任意数量的空格。