英国(GB)邮政编码验证,不需要正则表达式


United Kingdom (GB) postal code validation without regex

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

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

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

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

编辑:为了帮助未来的访问者并防止您发布更多的正则表达式,这里有一个我测试过的正则表达式(截至2013-04-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);

我是根据维基页面写这个答案的。

当检查验证部分时,似乎有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. 检查输入是否为异常,如果是,则返回valid
  3. 检查输入的长度是否为4 <长度< 8
  4. 检查邮政编码是否有效

最后一部分是棘手的,但我们将它分成三个部分的长度为一些概述:

  1. Length = 7: AA9A9AA and AA999AA
  2. 长度= 6: AA99AA , A9A9AA A999AA
  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;
}

注意我没有添加英国军队邮局和非地理代码。

用法:

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

由英国政府提供。

   (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})

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

存储邮政编码,获取用户输入或其他内容,并查看是否得到匹配。但你使解决方案比你想象的要复杂得多。我不得不使用实际的邮政编码来实现我想要的,但是为了简单的验证目的,就像"维护"一个正则表达式一样困难,存储数万或数十万(如果不是更多)并或多或少地实时验证是一项更加困难的任务。

如果一个小型分布式服务听起来像一个比正则表达式更有效的解决方案,那就去做吧,但我确信它不是。除非您需要根据英国邮政编码或类似的东西对您自己的数据进行地理空间查询,否则我怀疑DB存储是否是可行的解决方案。只是我的两分钱。

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

无可否认,该应用程序正在执行中等复杂性的地理空间查询,但仅存储需求就非常昂贵和苛刻。

底线,只要坚持使用正则表达式,并在两分钟内完成

我正在维基百科上查找英国邮政编码的链接。

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

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

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

(您还可以使用语法本身来强制或禁止某些文字和组合。只要对你来说更容易读懂或更容易理解。不同的人对这些事情的倾向不同。

这里有一个突出GOLD解析系统优点的页面。你可以用任何你喜欢的:我只是推荐这个,因为它很擅长它的工作,并且多年来一直在稳步改进。http://www.goldparser.org/about/why-use-gold.htm

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

如果您需要绝对数据,请考虑使用地形测量局开放数据倡议"Code-Point®Open"数据集,这是英国(我猜不是北爱尔兰)的大量数据点的CSV,其中一个是邮政编码。请注意,该文件为20MB,因此您可能必须将其转换为更易于管理的格式。

正则表达式很难调试,很难从一种正则表达式移植到另一种正则表达式(无声的"错误"),也很难更新。

对于大多数正则表达式来说都是正确的,但是为什么不把它分成多个部分呢?你可以很容易地把它分成六个部分,根据六个不同的一般规则,如果你考虑到所有的特殊情况,可能会更多。

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

是否可以选择第三方服务?

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

GeoNames数据库:

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

+1为"why care"注释。我不得不在各种项目中使用"官方"正则表达式,虽然我从来没有试图打破它,它的工作和它的工作。我已经在Java和PHP代码中使用了它,而不需要在regex格式之间进行转换。

是否有你必须调试或分解它的原因?

顺便说一下,这个正则表达式规则以前可以在维基百科上找到,但现在好像不见了。

编辑:关于空格/无空格的争论,邮政编码应该在有空格或没有空格的情况下有效。由于邮政编码的最后一部分(空格之后)总是三位数字,因此可以手动插入空格,这将允许您通过regex规则运行它。

取有效邮编列表,检查输入的邮编是否在其中