用于匹配英国邮政编码的正则表达式

239
我需要一个正则表达式来验证一个包含在输入字符串中的完整的英国邮编,包括所有不常见的邮编形式和通常的形式。例如:

匹配

  • CW3 9SS
  • SE5 0EG
  • SE50EG
  • se5 0eg
  • WC2H 7LT

不匹配

  • aWC2H 7LT
  • WC2H 7LTa
  • WC2H
如何解决这个问题?

2
@axrwkr,那看起来并不有帮助。 - Kieran Benton
8
英国邮编验证 - JavaScript 和 PHP 我无法使用接受的答案匹配有效的邮政编码,但我找到了这个,它可以匹配有效的邮政编码。对于客户端验证,JavaScript 版本可以直接使用;对于服务器端验证,将 JavaScript 重写为 C# 相对容易。它甚至重新格式化邮政编码以添加空格,因此如果你输入邮政编码 W1A1AA,它不仅会验证它,还会将其重新格式化为 W1A 1AA。它甚至处理了各种不寻常的来自英国领土的邮政编码。 - user2985029
2
提供的链接无法处理“AA1A 1AA”格式。参考:http://www.dhl.com.tw/content/dam/downloads/tw/express/forms/postcode_formats.pdf - Anthony Scaife
2
如果您只想验证邮政编码,我们提供一个免费(需要注册)的验证REST API端点 - http://developers.alliescomputing.com/postcoder-web-api/address-lookup/validate-postcode - Stephen Keable
1
好问题。我认为在您需要匹配的罕见示例列表中,包括曼彻斯特市中心邮政编码,例如“M1 3HZ”,是值得的。许多人不知道字母和数字组合的规则。 - Martin Joiner
1
这里的许多答案都基于英国政府提供的一个错误的正则表达式。有关这些问题的详细信息,请参阅我的答案 - ctwheels
33个回答

11

邮政编码可能会发生变化,唯一验证邮政编码的真正方法是拥有完整的邮政编码列表,并查看其中是否存在。

但正则表达式非常有用,因为它们:

  • 易于使用和实现
  • 短小精悍
  • 运行速度快
  • 相对于完整的邮政编码列表而言,较容易维护
  • 仍然可以捕获大多数输入错误

但是,正则表达式往往难以维护,特别是对于那些不是第一次创建它的人来说。因此,必须满足以下要求:

  • 尽可能易于理解
  • 相对未来具有一定的保证性

这意味着本答案中的大多数正则表达式都不够好。例如,我可以看到[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

对我来说,这是在尽可能验证的同时保证未来可靠性和易维护性之间找到的正确平衡。


不确定为什么你被投票否决了 - 这个代码可以处理我输入的所有有效邮政编码,而且它还能正确处理很多其他答案无法处理的空格。有人可以解释一下为什么吗? - Jon
1
@Jon 当其他字符附加到开头或结尾时,它也会匹配,例如“aSW1A 1AAasfg”对我来说是匹配的(虽然我没有投反对票,因为似乎很容易修复)。 - decvalts

10

我已经寻找英国邮编正则表达式一整天了,偶然发现了这个帖子。我试用了上面大部分建议的正则表达式,但都不适用于我,因此我自己想出了一个正则表达式,据我所知,它能够捕捉到所有有效的英国邮政编码(根据Royal Mail最新的文献,截止至2013年1月)。

下面是正则表达式和一些简单的邮政编码检查PHP代码。注意:它允许小写或大写的邮政编码以及GIR 0AA异常情况,但为了处理输入的邮政编码中很可能存在空格的情况,它还使用了一个简单的str_replace在测试正则表达式之前去除了空格。除此之外,任何差异,Royal Mail在他们的文献中甚至都没有提到它们(请参见http://www.royalmail.com/sites/default/files/docs/pdf/programmers_guide_edition_7_v5.pdf,从第17页开始阅读)!

注意:在Royal Mail自己的文献中(上面的链接),第三和第四位之间存在一些模糊性,以及如果这些字符是字母时所采取的例外情况。我直接联系了Royal Mail以澄清这一点,按照他们自己的话说,“带有格式AANA NAA的Outward Code中第4位的字母没有任何例外情况,而格式为ANA NAA的Outward Code中第3位例外情况仅适用于Outward Code的最后一个字母。”就是这样!

<?php

    $postcoderegex = '/^([g][i][r][0][a][a])$|^((([a-pr-uwyz]{1}([0]|[1-9]\d?))|([a-pr-uwyz]{1}[a-hk-y]{1}([0]|[1-9]\d?))|([a-pr-uwyz]{1}[1-9][a-hjkps-uw]{1})|([a-pr-uwyz]{1}[a-hk-y]{1}[1-9][a-z]{1}))(\d[abd-hjlnp-uw-z]{2})?)$/i';

    $postcode2check = str_replace(' ','',$postcode2check);

    if (preg_match($postcoderegex, $postcode2check)) {

        echo "$postcode2check is a valid postcode<br>";

    } else {

        echo "$postcode2check is not a valid postcode<br>";

    }

?>

我希望这篇文章能对其他遇到同样问题的人有所帮助。


1
我很想知道哪些示例邮政编码未能通过发布的测试? - Zhaph - Ben Duguid
Q点说得好 - 我犯了错误!然而,这变成了对第三/第四个字母的解释问题,我不确定我们哪一个是正确的。文档明确提到第一和第二个字母是“第一/第二个字母位置”,但第三个字母仅被称为“第三个位置”。我将其解释为邮政编码中的第三个字符(字母或数字)。否则,以上例子中的B可能会被翻译为第二个字母位置上的字母,从而使发布的正则表达式无效? - Dan Solo
1
刚刚从英国皇家邮政的支持团队那里得到消息,我的规则解释显然是正确的。外向编码中(例如AANA NAA),第四个位置的字母没有任何例外,第三个位置的例外仅适用于最后一个字母(例如ANA NAA)。这是直接听到的。 - Dan Solo
好知道 - 你可能想要更新你的答案,加入这个信息 ;) - Zhaph - Ben Duguid
1
@DanSolo 这个正则表达式将返回一个对有效邮政编码的前半部分进行匹配的结果,该邮政编码缺少内部代码,例如“SW1A”或“BD25”,没有后半部分(或至少对我来说是这样的)。 - decvalts
显示剩余3条评论

8
这是一个基于marcj回答中链接的文档指定格式的正则表达式:

以下是需要翻译的内容:

/^[A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9][A-Z]{2}$/

唯一的区别就是根据规范,最后2个字符不能是[CIKMOV]中的任何一个字符。
编辑: 这里有另一个版本,它确实测试了尾部字符的限制。
/^[A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9][A-BD-HJLNP-UW-Z]{2}$/

英国邮政编码比仅仅接受 A-Z 更加复杂 - 根据字符的位置,Q 从不被允许,V 只是偶尔使用。 - Zhaph - Ben Duguid
3
如果你只需要进行语法检查,那么这可能不重要。正如其他人所说,只有在一个最新的数据库中进行查找才能获得几乎正确的结果,但即使如此,仍然存在数据库更新的问题。因此,对我而言,这个语法检查器的正则表达式很清晰、简单且实用。 - Rick-777

5
一些上述的正则表达式有点过于严格。请注意真实的邮政编码:"W1K 7AA"将无法通过上述规则“位置3 - 只能使用AEHMNPRTVXY”而被拒绝,因为“K”是不允许的。
正则表达式:
^(GIR 0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKPS-UW])[0-9][ABD-HJLNP-UW-Z]{2})$

似乎更准确了,可以参考英国邮政编码维基百科文章
请注意,此正则表达式要求仅使用大写字符。
更重要的问题是,您是否限制用户输入以仅允许实际存在的邮政编码,还是仅试图阻止用户在表单字段中输入完全无用的内容。正确匹配每个可能的邮政编码并使其具有未来性是更困难的难题,除非您是HMRC,否则可能不值得。

看起来邮局已经前进了,但政府还有些滞后 :( - Zhaph - Ben Duguid
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})|(([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})$”。我喜欢它,因为它允许大小写,并使空格变为可选项,更易于使用,即使不是100%正确! - bigtv

5

虽然这里有很多答案,但我对它们都不太满意。大多数答案要么是错误的,要么过于复杂,要么根本不能用。

我看了一下@ctwheels的回答,我发现他的回答非常详细和正确;我们必须感谢他。但是对于我来说,这对于如此简单的事情来说还是“数据”太多了。

幸运的是,我设法获得了一个仅包含英格兰境内超过一百万活动邮政编码的数据库,并编写了一个小型PowerShell脚本来测试和基准测试结果。

英国邮政编码规范:有效邮政编码格式

这是“我的”正则表达式:

^([a-zA-Z]{1,2}[a-zA-Z\d]{1,2})\s(\d[a-zA-Z]{2})$

简短、简单、易懂。即使是最没有经验的人也能理解发生了什么。

说明:

^ asserts position at start of a line
    1st Capturing Group ([a-zA-Z]{1,2}[a-zA-Z\d]{1,2})
        Match a single character present in the list below [a-zA-Z]
        {1,2} matches the previous token between 1 and 2 times, as many times as possible, giving back as needed (greedy)
        a-z matches a single character in the range between a (index 97) and z (index 122) (case sensitive)
        A-Z matches a single character in the range between A (index 65) and Z (index 90) (case sensitive)
        Match a single character present in the list below [a-zA-Z\d]
        {1,2} matches the previous token between 1 and 2 times, as many times as possible, giving back as needed (greedy)
        a-z matches a single character in the range between a (index 97) and z (index 122) (case sensitive)
        A-Z matches a single character in the range between A (index 65) and Z (index 90) (case sensitive)
        \d matches a digit (equivalent to [0-9])
        \s matches any whitespace character (equivalent to [\r\n\t\f\v ])
    2nd Capturing Group (\d[a-zA-Z]{2})
        \d matches a digit (equivalent to [0-9])
        Match a single character present in the list below [a-zA-Z]
        {2} matches the previous token exactly 2 times
        a-z matches a single character in the range between a (index 97) and z (index 122) (case sensitive)
        A-Z matches a single character in the range between A (index 65) and Z (index 90) (case sensitive)
$ asserts position at the end of a line

结果(检查的邮政编码):

TOTAL OK: 1469193
TOTAL FAILED: 0
-------------------------------------------------------------------------
Days              : 0
Hours             : 0
Minutes           : 5
Seconds           : 22
Milliseconds      : 718
Ticks             : 3227185939
TotalDays         : 0.00373516891087963
TotalHours        : 0.0896440538611111
TotalMinutes      : 5.37864323166667
TotalSeconds      : 322.7185939
TotalMilliseconds : 322718.5939

1
谢谢@Mecanik - 这正是我所需要的!不过,对于我的实现,我必须将空格设置为可选项:^([a-zA-Z]{1,2}[a-zA-Z\d]{1,2})\s?(\d[a-zA-Z]{2})$ - Jon Humphrey

5

我需要一个简单的正则表达式,可以允许输入过多的内容,但不应拒绝一个有效的邮政编码。我选择了这个正则表达式(输入为去除空格的字符串):

/^([a-z0-9]\s*){5,8}$/i

这使得最短的邮政编码像"L1 8JQ"和最长的邮政编码像"OL14 5ET"都可以使用。因为它允许最多8个字符,所以如果没有空格,它也会允许不正确的8位邮政编码,例如"OL145ETX"。但是,这只是一个简单的正则表达式,仅适用于那些足够简单的情况。

抱歉,我昨天测试时可能漏掉了/i。 - ProgrammingLlama

4

基本规则:

^[A-Z]{1,2}[0-9R][0-9A-Z]? [0-9][ABD-HJLNP-UW-Z]{2}$

英国的邮政编码(或称为邮编)由五到七个字母数字字符组成,中间用空格分隔。涉及特定位置可以出现哪些字符的规则相当复杂,充满例外情况。因此,刚刚展示的正则表达式遵循基本规则。
完整规则:
如果您需要一个正则表达式以牺牲可读性为代价来满足邮政编码规则的所有要求,请使用以下内容。
^(?:(?:[A-PR-UWYZ][0-9]{1,2}|[A-PR-UWYZ][A-HK-Y][0-9]{1,2}|[A-PR-UWYZ][0-9][A-HJKSTUW]|[A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRV-Y]) [0-9][ABD-HJLNP-UW-Z]{2}|GIR 0AA)$

来源: https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s16.html

已针对我们客户数据库进行测试,似乎非常准确。


4

我使用以下正则表达式,我已经测试过它针对所有有效的英国邮政编码。它基于推荐规则,但尽可能简洁,并且不使用任何特殊的语言特定的正则表达式规则。

([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”邮政编码被排除在外,并且不会通过验证,因为它不在官方邮局邮政编码列表中,据我所知,也不会用作注册地址。如果需要,将其添加为特殊情况应该很容易。

4

以下是我们处理英国邮政编码问题的方法:

^([A-Za-z]{1,2}[0-9]{1,2}[A-Za-z]?[ ]?)([0-9]{1}[A-Za-z]{2})$
说明:
  • 期望1或2个a-z字符,大小写均可
  • 期望1或2个数字
  • 期望0或1个a-z字符,大小写均可
  • 允许空格
  • 期望1个数字
  • 期望2个a-z字符,大小写均可

这可以涵盖大多数格式,然后我们使用数据库来验证邮政编码是否真实存在,这些数据由openpoint驱动 https://www.ordnancesurvey.co.uk/opendatadownload/products.html

希望这能有所帮助


这允许格式AANNA NAA,这是无效的。 - ctwheels
因此,“这是最通用的格式”是答案的一部分。 :) - Alex Stephens

3

如果您不想在验证后再在服务器端对其进行修剪,那么此选项允许两侧的空格和制表符为空。

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

这是我使用c#(System.Text.RegularExpressions)在原问题描述的示例中工作的唯一模式。 - MattjeS
这是英国政府的错误正则表达式,它无法验证一些有效格式。 - ctwheels
@ctwheels 你好,请提供一个无法通过的邮政编码,谢谢。 - Matas Vaitkevicius
“AAA 1AA” 不是有效的格式:请参考我的答案了解解释和修复方法。 - ctwheels

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接