13

我已经尝试了几个正则表达式,但仍然有一些有效的邮政编码有时会被拒绝。

搜索互联网、维基百科和 SO,我只能找到正则表达式验证解决方案。

是否有不使用正则表达式的验证方法?在任何语言中,我想它都会很容易移植。

我想最简单的方法是与邮政编码数据库进行比较,但这需要从可靠的来源定期维护和更新。

编辑:为了帮助未来的访问者并防止您发布更多正则表达式,这是我测试过的正则表达式(截至 2013 年 4 月 24 日),适用于 Code Point 中的所有邮政编码(请参阅@Mikkel Løkke 的回答):

//PHP PCRE (it was on Wikipedia, it isn't there anymore; I might have modified it, don't remember).
$strPostalCode=preg_replace("/[\s]/", "", $strPostalCode);
$bValid=preg_match("/^(GIR 0AA)|(((A[BL]|B[ABDHLNRSTX]?|C[ABFHMORTVW]|D[ADEGHLNTY]|E[HNX]?|F[KY]|G[LUY]?|H[ADGPRSUX]|I[GMPV]|JE|K[ATWY]|L[ADELNSU]?|M[EKL]?|N[EGNPRW]?|O[LX]|P[AEHLOR]|R[GHM]|S[AEGKLMNOPRSTY]?|T[ADFNQRSW]|UB|W[ADFNRSV]|YO|ZE)[1-9]?[0-9]|((E|N|NW|SE|SW|W)1|EC[1-4]|WC[12])[A-HJKMNPR-Y]|(SW|W)([2-9]|[1-9][0-9])|EC[1-9][0-9])[0-9][ABD-HJLNP-UW-Z]{2})$/i", $strPostalCode);
4

8 回答 8

22

我正在根据wiki page写这个答案。

检查验证部分时,似乎有 6 种格式(A = 字母和 9 = 数字):

AA9A 9AA                       AA9A9AA                   AA9A9AA
A9A 9AA     Removing space     A9A9AA       order it     AA999AA
A9 9AA    ------------------>  A99AA     ------------->  AA99AA
A99 9AA                        A999AA                    A9A9AA
AA9 9AA                        AA99AA                    A999AA
AA99 9AA                       AA999AA                   A99AA

正如我们所看到的,长度可能从 5 到 7 不等,如果我们愿意,我们必须考虑一些特殊情况。

所以我们正在编写的函数必须执行以下操作:

  1. 删除空格并转换为大写(或小写)。
  2. 检查输入是否为异常,如果是则应返回有效
  3. 检查输入的长度是否为 4 < length < 8。
  4. 检查它是否是有效的邮政编码。

最后一部分很棘手,但我们将按长度将其分成 3 个部分以进行概述:

  1. 长度 = 7:AA9A9AAAA999AA
  2. 长度 = 6:AA99AAA9A9AAA999AA
  3. 长度 = 5:A99AA

为此,我们将使用switch(). 从现在开始,只需逐个字符地检查它是否是正确位置的字母或数字。

那么让我们来看看我们的PHP实现:

function check_uk_postcode($string){
    // Start config
    $valid_return_value = 'valid';
    $invalid_return_value = 'invalid';
    $exceptions = array('BS981TL', 'BX11LT', 'BX21LB', 'BX32BB', 'BX55AT', 'CF101BH', 'CF991NA', 'DE993GG', 'DH981BT', 'DH991NS', 'E161XL', 'E202AQ', 'E202BB', 'E202ST', 'E203BS', 'E203EL', 'E203ET', 'E203HB', 'E203HY', 'E981SN', 'E981ST', 'E981TT', 'EC2N2DB', 'EC4Y0HQ', 'EH991SP', 'G581SB', 'GIR0AA', 'IV212LR', 'L304GB', 'LS981FD', 'N19GU', 'N811ER', 'NG801EH', 'NG801LH', 'NG801RH', 'NG801TH', 'SE18UJ', 'SN381NW', 'SW1A0AA', 'SW1A0PW', 'SW1A1AA', 'SW1A2AA', 'SW1P3EU', 'SW1W0DT', 'TW89GS', 'W1A1AA', 'W1D4FA', 'W1N4DJ');
    // Add Overseas territories ?
    array_push($exceptions, 'AI-2640', 'ASCN1ZZ', 'STHL1ZZ', 'TDCU1ZZ', 'BBND1ZZ', 'BIQQ1ZZ', 'FIQQ1ZZ', 'GX111AA', 'PCRN1ZZ', 'SIQQ1ZZ', 'TKCA1ZZ');
    // End config


    $string = strtoupper(preg_replace('/\s/', '', $string)); // Remove the spaces and convert to uppercase.
    $exceptions = array_flip($exceptions);
    if(isset($exceptions[$string])){return $valid_return_value;} // Check for valid exception
    $length = strlen($string);
    if($length < 5 || $length > 7){return $invalid_return_value;} // Check for invalid length
    $letters = array_flip(range('A', 'Z')); // An array of letters as keys
    $numbers = array_flip(range(0, 9)); // An array of numbers as keys

    switch($length){
        case 7:
            if(!isset($letters[$string[0]], $letters[$string[1]], $numbers[$string[2]], $numbers[$string[4]], $letters[$string[5]], $letters[$string[6]])){break;}
            if(isset($letters[$string[3]]) || isset($numbers[$string[3]])){
                return $valid_return_value;
            }
        break;
        case 6:
            if(!isset($letters[$string[0]], $numbers[$string[3]], $letters[$string[4]], $letters[$string[5]])){break;}
            if(isset($letters[$string[1]], $numbers[$string[2]]) || isset($numbers[$string[1]], $letters[$string[2]]) || isset($numbers[$string[1]], $numbers[$string[2]])){
                return $valid_return_value;
            }
        break;
        case 5:
            if(isset($letters[$string[0]], $numbers[$string[1]], $numbers[$string[2]], $letters[$string[3]], $letters[$string[4]])){
                return $valid_return_value;
            }
        break;
    }

    return $invalid_return_value;
}

请注意,我没有添加British Forces Post Office非地理代码

用法:

echo check_uk_postcode('AE3A 6AR').'<br>'; // valid
echo check_uk_postcode('Z9 9BA').'<br>'; // valid
echo check_uk_postcode('AE3A6AR').'<br>'; // valid
echo check_uk_postcode('EE34      6FR').'<br>'; // valid
echo check_uk_postcode('A23A 7AR').'<br>'; // invalid
echo check_uk_postcode('A23A   7AR').'<br>'; // invalid
echo check_uk_postcode('WA3334E').'<br>'; // invalid
echo check_uk_postcode('A2 AAR').'<br>'; // invalid
于 2013-04-30T15:42:21.973 回答
6

由英国政府提供。

   (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-HJKSTUW])|([A-Z-[QVX]][A-Z-[IJZ]][0-9][ABEHMNPRVWXY])))) [0-9][A-Z-[CIKMOV]]{2})

我使用从HERE获得的邮政编码构建了仅基于伦敦邮政编码的应用程序。但老实说,即使只有伦敦邮政编码,您也需要比必要更多的存储空间。当然,这个想法是微不足道的。

存储邮政编码,获取用户输入或其他内容,然后查看是否匹配。但是您使解决方案复杂化的程度远远超出您的想象。我必须使用实际的邮政编码来实现我想要的,但为了简单的验证目的,就像“维护”一个正则表达式一样难,存储数万或数十万(如果不是更多)并实时或多或少地验证是一项艰巨得多的任务。

如果迷你分布式服务听起来比正则表达式更有效,那就去吧,但我敢肯定它不是。除非您需要针对英国邮政编码或类似的东西对您自己的数据进行地理空间查询,否则我怀疑数据库存储是一个可行的解决方案。只是我的2美分。

更新

根据指数,英国有 1,758,417 个邮政编码。我可以告诉你,我正在使用一些 Mongo 集群(Amazon EC2 高内存实例)来提供可靠的仅限伦敦的服务(仅索引伦敦邮政编码),这是一件相当昂贵的事情,即使使用基本存储也是如此。

诚然,该应用程序正在执行中等复杂度的地理空间查询,但仅存储需求就非常昂贵且要求很高。

最重要的是,只需坚持正则表达式并在两分钟内完成。

于 2013-04-22T15:17:35.943 回答
2

我现在正在查看维基百科中的英国邮政编码链接。

http://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom

验证部分列出了六种由字母和数字组合而成的格式。然后在下面的注释中有更多信息。我会尝试的第一件事是使用 GoldParserBuilder 之类的工具的 BNF 类型语法。您可以以更易读的格式描述基本格式,并自动生成高效的解析器和词法分析器。过去,我已经成功地使用这些工具来避免编写庞大、丑陋的正则表达式。

从那时起,该程序就有了一个格式正确的已知类型的邮政编码。此时,特定的数字或字母可能会违反某些内容。每种类型的邮政编码都可以有一个编程功能来查找该特定类型的违规行为。最终产品将包含一个自动生成的解析器,该解析器将未经验证但结构化/已识别的邮政编码传递给专用的验证功能。然后,您可以从那里重构或优化。

(您也可以使用语法本身来强制或禁止某些文字和组合。任何对您来说更具可读性或可理解性的东西。不同的人倾向于这些事物的不同目的。)

这是一个突出 GOLD Parsing System 优点的页面。您可以使用任何您喜欢的:我只是推广这个 b/c,它的工作很好,并且多年来一直在稳步改进。 http://www.goldparser.org/about/why-use-gold.htm

于 2013-04-17T23:21:13.387 回答
2

我认为 RegEX,虽然冗长可能是最好的解决方案,如果你想做的只是验证某些东西是否可以是有效的英国邮政编码。

如果您需要绝对数据,请考虑使用 Ordnance Survey OpenData 倡议“ Code-Point® Open”数据集,它是英国(所以我猜不是北爱尔兰)大量数据点的 CSV,其中一个是邮政编码。请注意,该文件为 20MB,因此您可能需要将其转换为更易于管理的格式。

于 2013-04-22T11:13:22.017 回答
2

正则表达式难以调试,难以从一种正则表达式风格移植到另一种风格(无声的“错误”),并且难以更新。

大多数正则表达式都是如此,但你为什么不把它分成多个部分呢?对于六种不同的一般规则,您可以轻松地将其分成六个部分,如果考虑到所有特殊情况,甚至可能更多。

使用简单的正则表达式创建一个注释良好的 20 行方法很容易调试(每行一个简单的正则表达式)并且也易于更新。移植问题是相同的,但另一方面,您不需要使用一些花哨的语法库。

于 2013-04-22T15:39:36.067 回答
1

第三方服务是一种选择吗?

http://www.postcodeanywhere.co.uk/address-validation/

地名数据库:

http://www.geonames.org/postal-codes/

于 2013-04-22T16:02:28.590 回答
1

为“为什么关心”评论+1。我不得不在各种项目中使用“官方”正则表达式,虽然我从未尝试过将其分解,但它可以工作并且可以完成工作。我已经将它与 Java 和 PHP 代码一起使用,而无需在正则表达式格式之间进行转换。

您是否有理由必须对其进行调试或分解?

顺便说一句,以前在维基百科上可以找到正则表达式规则,但它似乎已经消失了。

编辑:至于空格/无空格的辩论,无论有没有空格,邮政编码都应该是有效的。由于邮政编码的最后一部分(空格之后)总是三位数,因此可以手动插入空格,然后您可以通过正则表达式规则运行它。

于 2013-04-29T11:50:46.553 回答
0

获取有效邮政编码列表并检查输入的邮政编码是否在其中。

于 2013-04-22T15:29:13.817 回答