没有正则表达式的英国(GB)邮政编码验证

时间:2013-04-11 22:46:05

标签: php postal-code

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

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

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

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

编辑:为了帮助未来的访问者并阻止您发布任何更多的正则表达式,这是我测试的一个正则表达式(截至2013-04-24),适用于Code Point中的所有邮政编码(see @Mikkel Løkke's answer) :

//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);

8 个答案:

答案 0 :(得分:21)

我正在根据wiki page撰写此答案。

检查validation part时,似乎有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之间变化,如果需要,我们必须考虑一些special cases

因此我们编码的函数必须执行以下操作:

  1. 删除空格并转换为大写(或小写)。
  2. 检查输入是否为异常,如果是,则应返回有效
  3. 检查输入的长度是否为4 < 长度< 8。
  4. 检查它是否是有效的邮政编码。
  5. 最后一部分很棘手,但我们会按长度分为3个部分进行概述:

    1. 长度= 7: AA9A9AA AA999AA
    2. 长度= 6: AA99AA A9A9AA A999AA
    3. 长度= 5: A99AA
    4. 为此,我们将使用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 Officenon-geographic codes

      用法:

      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
      

答案 1 :(得分: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美分。

<强>更新

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

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

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

答案 2 :(得分: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

答案 3 :(得分:2)

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

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

答案 4 :(得分:2)

  

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

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

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

答案 5 :(得分:1)

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

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

地理名称数据库:

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

答案 6 :(得分:1)

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

你有必要调试它或将其分解吗?

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

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

答案 7 :(得分:0)

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