如何通过前缀正确匹配英国邮政编码?

4
我有多家餐厅都提供送餐服务到伦敦的某些邮政编码区域,例如:
  • EC1
  • WC1
  • WC2
  • W1
当有人搜索能够送餐到他们家的餐厅时,他们会输入完整的邮政编码。
有些人正确地输入带有空格的邮政编码,而有些人只是输入所有字母和数字,没有空格分隔符。为了使事情协调一致,我会在尝试匹配之前删除邮政编码中的任何空格。
到目前为止,我过去只是通过检查它是否以相关前缀开头来将邮政编码与前缀进行匹配,但后来我意识到这并不是万无一失的:
  • WC1E123 => WC1 的正确匹配
  • W1ABC => W1 的正确匹配
  • W10ABC => 不正确的 W1 匹配,应该只匹配 W10 前缀
在不失败上述的 W1 / W10 测试的情况下,我如何知道给定一个没有空格的完整邮政编码是否与给定前缀匹配?
是否有任何解决问题的方法,而不涉及强制客户在正确位置输入带有空格的邮政编码?

@DmitriChubarov 我的问题与我之前阅读过的这个问题无关。他的问题是关于验证 邮政编码,而我的问题是关于将邮政编码与前缀进行匹配。两件非常不同的事情。你提供的链接也是关于验证邮政编码。 - BenMorel
好的。你能把你当前用来匹配前缀的正则表达式发一下吗?在我看来,官方的正则表达式需要做出一些微小的修改才能将前缀和后缀分开匹配。另外,我建议保留我的上一个评论,因为那个问题似乎很相关。在这里放个链接也是不错的选择。 - Dima Chubarov
2
你是否有什么原因不能先测试W10,再测试W1呢?这是处理一个前缀是另一个子集的情况的常规方法...先测试最长的那个。 - Phil Perry
你能否拥有所有代码的数据库?如果可以,那么你可以继续查询数据库,直到找到单个正确的匹配项,或者在没有更多匹配项的情况下从列表中取第一个。比如,如果用户输入W1ABC,那么你可以以W开始查询数据库,这样你会得到多条记录,然后以W1为关键字再次查询,你会得到多条记录,如W1、W10、W11,但是对于W1A,你将不会得到任何记录,所以在这种情况下,从W1、W10、W11中选择第一条记录,也就是W1作为答案。现在对于W10ABC,按照相同的模式,你将得到W10的一条记录,这就是你的答案。我不知道它是否涵盖了所有情况,但请考虑一下。 - cjd
@cjd 英国邮政编码数据库非常庞大,而且随着时间的推移可能会发生变化,所以对于我的使用情况来说并不是一个可行的解决方案! - BenMorel
显示剩余5条评论
6个回答

16

英国邮编有6种可能的格式:

  • A9 9AA
  • A9A 9AA
  • A99 9AA
  • AA9 9AA
  • AA9A 9AA
  • AA99 9AA

我认为你的解决方案需要包含两部分。第一部分是验证输入;第二部分是获取第一部分。

验证

这非常重要,即使您已经说过您并不打算这样做,但是如果没有它,您将很难获得正确的前缀,并可能会将您的司机送到错误的位置!

您可以有几种方法来验证它,可以使用第三方帮助您捕获完整且正确的地址(许多可用,包括http://www.qas.co.uk/knowledge-centre/product-information/address-postcode-finder.htm(我的公司)),或者至少使用一些正则表达式/类似的测试来验证邮政编码-例如Dmitri提供给您的链接。

如果您查看您列出的测试用例-W1ABC和W10ABC都不是有效的邮政编码-如果我们正确理解了这一点,那么下一个步骤就变得容易得多了。

提取前缀

假设您现在拥有一个完整的有效邮政编码,那么仅获取第一部分(outcode)就变得容易得多-无论是否有空格。因为第二半部分(incode)具有标准格式9AA,即数字-alpha-alpha,我会通过检测并删除此格式来完成它,从而仅留下您的前缀,无论是来自W1 0AA的W1还是来自W10 0AA的W10。

或者,如果您正在使用第三方捕获地址,则其中大多数将能够为您单独返回incode和outcode。


看起来大家都对你的回答很满意,所以你得到了应得的赏金;-) - BenMorel

2
下面的图形说明了英国邮政编码的格式: Format of UK postcodes 来源:https://www.getthedata.com/postcode(我的网站)。因此,您可以看到您需要的Outcode是您无空格的邮政编码减去最后三个字符。
在PHP中,可以这样写:
$outcode = substr($postcode_no_space, 0, -3)

当然,这并没有帮助验证邮政编码,但正如您在评论中指出的那样,问题不是关于验证。

感谢您的答复和图形,迟到总比没有好:) 话虽如此,当时我的原始要求也是能够基于不完整的前缀进行匹配。当我住在伦敦时,人们会说“我住在WC2”,即使WC2不是一个有效的前缀; WC2E是。因此,我希望能够匹配这种不完整的邮政编码前缀,而不陷入W1-W11的困境中。 - BenMorel
如果是这种情况,你可能需要一个算法而不是单一的规则。假设你有一个正式的 $outcode 和一个非正式的前缀 $prefix(例如 WC2),那么你首先检查是否存在精确匹配 $outcode==$prefix,如果失败,则从 $outcode 中移除最后一个字符(WC2E 变成 WC2)并测试是否与 $prefix 匹配。 - Dan Winchester

1
我使用以下正则表达式,仅匹配前缀部分,但使用先行断言确保完整的邮政编码有效(包括可选的空格)。
(GIR|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW]))(?=( )?[0-9][ABD-HJLNP-UW-Z]{2})

虽然它会匹配一些无效的邮编(例如以AA开头等),但如果您只是用它来查找前缀,它应该可以解决问题。

另外,我注意到英国政府提供的正则表达式已经更新,因此可以进行更新:

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

1

在php中,我执行以下操作

$first=trim(substr(trim($postcode),0,-3));

获取邮政编码的第一部分。 我已经使用它多年了,它很有效。 不管用户是否在中间包括空格(或2个空格),因为最后一部分始终是3个字符。 我在一家分销公司工作,我们需要支付更多费用来送达某些邮政编码区域。 如果有人输入他们的邮政编码不正确,您将会面临问题,如果他们从末尾省略一个字符。

如果以上方法不够好。

您可以验证用户提供的邮政编码是否有效,然后http://postcodes.io/ 可以帮助您。

http://api.postcodes.io/postcodes/W11%202AQ 将返回一些JSON,其中包含邮政编码的有效性。

{
    "status": 200,
    "result": {
        "postcode": "W11 2AQ",
        "quality": 1,
        "eastings": 524990,
        "northings": 181250,
        "country": "England",
        "nhs_ha": "London",
        "longitude": -0.200056238526337,
        "latitude": 51.5163540527233,
        "parliamentary_constituency": "Kensington",
        "european_electoral_region": "London",
        "primary_care_trust": "Kensington and Chelsea",
        "region": "London",
        "lsoa": "Kensington and Chelsea 004A",
        "msoa": "Kensington and Chelsea 004",
        "nuts": "Colville",
        "incode": "2AQ",
        "outcode": "W11",
        "admin_district": "Kensington and Chelsea",
        "parish": "Kensington and Chelsea, unparished area",
        "admin_county": null,
        "admin_ward": "Colville",
        "ccg": "NHS West London (Kensington and Chelsea, Queenís Park and Paddington)",
        "codes": {
            "admin_district": "E09000020",
            "admin_county": "E99999999",
            "admin_ward": "E05009392",
            "parish": "E43000210",
            "ccg": "E38000202"
        }
    }
}

JSON的一部分是"outcode": "W11",我认为这正是您要寻找的。

您还可以使用“eastings”:524990,“northings”:181250字段来计算从餐厅到用户的直线距离。单位为米。使用勾股定理。


我觉得您在问题中忽略了一些复杂性... :) - BenMorel

0

由于您可以计算客户输入的邮政编码的长度,而邮政编码的格式总是以9AA结尾,因此您可以将代码分解为几种情况,并通过执行以下操作返回匹配项

firstPart -> postcode with last 3 characters removed
firstPartLength -> length of firstPart
switch (firstPartLength){
    case 2:
        code to compare prefix against A99AA format
    case 3:
        code to compare prefix against A9A9AA, A999AA, AA99AA format
    case 4:
        code to compare prefix against AA999AA format

或者如果您不想截断最后3个字符,

length -> length of postcode
switch (length){
    case 5:
        code to compare prefix against A99AA format
    case 6:
        code to compare prefix against A9A9AA, A999AA, AA99AA format
    case 7:
        code to compare prefix against AA999AA format

您还没有接受答案。您是否需要我们提供更多帮助,以便得到您想要的答案? - Josh Durham
我刚刚开始了一个悬赏,尽管我已经有了一个可能令人满意的答案,但是我想让更多的人提出他们的想法/评论,所以即使我现在没有更多的问题,我会一直保持它到最后,以便给它最好的机会,如果不是进一步的答案,也许是对现有答案的评论/投票! - BenMorel
好的!我只是想确保我们没有遗漏你需要的任何东西。 - Josh Durham

0

假设每个邮政编码都以 9AA 结尾,且每个输入的邮政编码都是有效的,则可以使用以下正则表达式来匹配区域前缀:

^(\w{2,4})\s*[0-9][a-zA-Z]{2}$

第一个捕获组返回所需的前缀。


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