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

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个回答

3

如果要按照英国皇家邮政的程序员指南检查邮政编码格式是否正确:

          |----------------------------outward code------------------------------| |------inward code-----|
#special↓       α1        α2    AAN  AANA      AANN      AN    ANN    ANA (α3)        N         AA
^(GIR 0AA|[A-PR-UWYZ]([A-HK-Y]([0-9][A-Z]?|[1-9][0-9])|[1-9]([0-9]|[A-HJKPSTUW])?) [0-9][ABD-HJLNP-UW-Z]{2})$

doogal.co.uk上的所有邮政编码都匹配,除了那些不再使用的。

在空格后添加?并使用不区分大小写的匹配来回答这个问题:

'se50eg'.match(/^(GIR 0AA|[A-PR-UWYZ]([A-HK-Y]([0-9][A-Z]?|[1-9][0-9])|[1-9]([0-9]|[A-HJKPSTUW])?) ?[0-9][ABD-HJLNP-UW-Z]{2})$/ig);
Array [ "se50eg" ]

3

邮政编码的前半部分 有效格式

  • [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]

特例
位置1 - 不使用QVX
位置2 - 除了GIR 0AA外不使用IJZ
位置3 - 只使用AEHMNPRTVXY
位置4 - 使用ABEHMNPRVWXY

邮政编码的后半部分

  • [0-9][A-Z][A-Z]

特例
位置2+3 - 不使用CIKMOV

请记住,并非所有可能的编码都被使用,因此此列表是有效编码的必要但不充分条件。只匹配所有有效编码的列表可能更容易?


3

通过经验测试和观察,以及与https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Validation确认,这是我编写的一个Python正则表达式版本,可以正确解析和验证英国邮政编码:

UK_POSTCODE_REGEX = r'(?P<postcode_area>[A-Z]{1,2})(?P<district>(?:[0-9]{1,2})|(?:[0-9][A-Z]))(?P<sector>[0-9])(?P<postcode>[A-Z]{2})'

这个正则表达式很简单并且有捕获组。它并没有包括所有合法英国邮政编码的验证,但只考虑了字母与数字的位置关系。

以下是我如何在代码中使用它:

@dataclass
class UKPostcode:
    postcode_area: str
    district: str
    sector: int
    postcode: str

    # https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Validation
    # Original author of this regex: @jontsai
    # NOTE TO FUTURE DEVELOPER:
    # Verified through empirical testing and observation, as well as confirming with the Wiki article
    # If this regex fails to capture all valid UK postcodes, then I apologize, for I am only human.
    UK_POSTCODE_REGEX = r'(?P<postcode_area>[A-Z]{1,2})(?P<district>(?:[0-9]{1,2})|(?:[0-9][A-Z]))(?P<sector>[0-9])(?P<postcode>[A-Z]{2})'

    @classmethod
    def from_postcode(cls, postcode):
        """Parses a string into a UKPostcode

        Returns a UKPostcode or None
        """
        m = re.match(cls.UK_POSTCODE_REGEX, postcode.replace(' ', ''))

        if m:
            uk_postcode = UKPostcode(
                postcode_area=m.group('postcode_area'),
                district=m.group('district'),
                sector=m.group('sector'),
                postcode=m.group('postcode')
            )
        else:
            uk_postcode = None

        return uk_postcode


def parse_uk_postcode(postcode):
    """Wrapper for UKPostcode.from_postcode
    """
    uk_postcode = UKPostcode.from_postcode(postcode)
    return uk_postcode

以下是单元测试:

@pytest.mark.parametrize(
    'postcode, expected', [
        # https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom#Validation
        (
            'EC1A1BB',
            UKPostcode(
                postcode_area='EC',
                district='1A',
                sector='1',
                postcode='BB'
            ),
        ),
        (
            'W1A0AX',
            UKPostcode(
                postcode_area='W',
                district='1A',
                sector='0',
                postcode='AX'
            ),
        ),
        (
            'M11AE',
            UKPostcode(
                postcode_area='M',
                district='1',
                sector='1',
                postcode='AE'
            ),
        ),
        (
            'B338TH',
            UKPostcode(
                postcode_area='B',
                district='33',
                sector='8',
                postcode='TH'
            )
        ),
        (
            'CR26XH',
            UKPostcode(
                postcode_area='CR',
                district='2',
                sector='6',
                postcode='XH'
            )
        ),
        (
            'DN551PT',
            UKPostcode(
                postcode_area='DN',
                district='55',
                sector='1',
                postcode='PT'
            )
        )
    ]
)
def test_parse_uk_postcode(postcode, expected):
    uk_postcode = parse_uk_postcode(postcode)
    assert(uk_postcode == expected)

2
为了扩充这个列表,我提供一个更实用的正则表达式,允许用户输入一个空字符串:empty string
^$|^(([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,1}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))$

这个正则表达式允许大写和小写字母,并在它们之间可选地添加空格。

从软件开发者的角度来看,这个正则表达式对于地址可能是可选的软件非常有用。例如,如果用户不想提供他们的地址详细信息。


1

请查看此页面上的Python代码:

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

我需要进行一些邮政编码解析工作。要求非常简单;我需要将邮政编码解析为一个outcode和(可选的)incode。好消息是,我不需要执行任何验证 - 我只需以一种模糊而智能的方式分割提供给我的信息即可。我不能假设导入格式方面有什么特别的要求,例如大小写和嵌入空格。但这并非坏消息;坏消息是我必须在RPG中完成所有操作。 :-(
尽管如此,我还是编写了一个小型的Python函数来澄清我的想法。
我已经用它来处理我的邮政编码。

1

我有用于验证英国邮政编码的正则表达式。

这适用于所有类型的邮政编码,无论是内部还是外部。

^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([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][ABEHMNPRVWXY]))) || ^((GIR)[ ]?(0AA))$|^(([A-PR-UWYZ][0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][0-9][0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][A-HK-Y0-9][0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][A-HK-Y0-9][0-9][0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][0-9][A-HJKS-UW0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$|^(([A-PR-UWYZ][A-HK-Y0-9][0-9][ABEHMNPRVWXY0-9])[ ]?([0-9][ABD-HJLNPQ-UW-Z]{0,2}))$

这适用于所有格式。

例如:

AB10-------------------->仅外部邮编

A1 1AA------------------>(外部和内部)邮编的组合

WC2A-------------------->外部


0

被接受的答案反映了皇家邮政给出的规则,尽管正则表达式中有一个错字。这个错字似乎也存在于gov.uk网站上(因为它在XML存档页面中)。

在A9A 9AA格式中,规则允许第三个位置有一个P字符,而正则表达式不允许。正确的正则表达式应该是:

(GIR 0AA)|((([A-Z-[QVX]][0-9][0-9]?)|(([A-Z-[QVX]][A-Z-[IJZ]][0-9][0-9]?)|(([A-Z-[QVX]][0-9][A-HJKPSTUW])|([A-Z-[QVX]][A-Z-[IJZ]][0-9][ABEHMNPRVWXY])))) [0-9][A-Z-[CIKMOV]]{2}) 

缩短后的正则表达式如下(使用Perl/Ruby语法):
(GIR 0AA)|([A-PR-UWYZ](([0-9]([0-9A-HJKPSTUW])?)|([A-HK-Y][0-9]([0-9ABEHMNPRVWXY])?))\s?[0-9][ABD-HJLNP-UW-Z]{2})

它还包括第一个和第二个块之间的可选空格。


0

我们收到了一个规范:

英国邮政编码必须符合以下形式之一(有一个例外,见下文):
    § A9 9AA
    § A99 9AA
    § AA9 9AA
    § AA99 9AA
    § A9A 9AA
    § AA9A 9AA
其中,A代表字母字符,9代表数字字符。
对于字母字符,还有以下附加规则:
    § 第1位的字符不能是Q、V或X
    § 第2位的字符不能是I、J或Z
    § 第3位的字符不能是I、L、M、N、O、P、Q、R、V、X、Y或Z
    § 第4位的字符不能是C、D、F、G、I、J、K、L、O、Q、S、T、U或Z
    § 最后两位的字符不能是C、I、K、M、O或V
不遵循这些通用规则的一个例外是邮政编码“GIR 0AA”,它是一个特殊的有效邮政编码。

我们想出了这个:

/^([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

但请注意 - 这允许在组之间有任意数量的空格。

2
抱歉,paulslater19,您的解决方案允许A99A 9AA邮政编码。 - user1854089

0

我在大量转移PDF和维基百科网站上发现的所有变体和正则表达式中,特别是维基百科的正则表达式中,第一个|(竖线)后面需要有一个^。我通过测试AA9A 9AA来弄清楚这一点,因为否则对于A9A 9AA的格式检查将会验证它。例如,检查EC1D 1BB,它应该是无效的,但返回的结果是有效的,因为C1D 1BB是一个有效的格式。

以下是我想出的一个好的正则表达式:

^([G][I][R] 0[A]{2})|^((([A-Z-[QVX]][0-9]{1,2})|([A-Z-[QVX]][A-HK-Y][0-9]{1,2})|([A-Z-[QVX]][0-9][ABCDEFGHJKPSTUW])|([A-Z-[QVX]][A-HK-Y][0-9][ABEHMNPRVWXY])) [0-9][A-Z-[CIKMOV]]{2})$

0
以下方法将检查邮政编码并提供完整信息。
const isValidUKPostcode = postcode => {
    try {
        postcode = postcode.replace(/\s/g, "");
        const fromat = postcode
            .toUpperCase()
            .match(/^([A-Z]{1,2}\d{1,2}[A-Z]?)\s*(\d[A-Z]{2})$/);
        const finalValue = `${fromat[1]} ${fromat[2]}`;
        const 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})|(([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})$/i;
        return {
            isValid: regex.test(postcode),
            formatedPostCode: finalValue,
            error: false,
            message: 'It is a valid postcode'
        };
    } catch (error) {
        return { error: true , message: 'Invalid postcode'};
    }
};

console.log(isValidUKPostcode('GU348RR'))
{isValid: true, formattedPostcode: "GU34 8RR", error: false, message: "It is a valid postcode"}

console.log(isValidUKPostcode('sdasd4746asd'))
{error: true, message: "Invalid postcode!"}

valid_postcode('787898523')
result => {error: true, message: "Invalid postcode"}

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