Проверка почтового индекса в Великобритании (ГБ) без регулярных выражений
Я пробовал несколько регулярных выражений, но некоторые действительные почтовые индексы иногда отклоняются.
Поиск в Интернете, Википедии и 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);
8 ответов
Я пишу этот ответ на основе вики-страницы.
При проверке части проверки кажется, что существует 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, и мы должны принять во внимание некоторые особые случаи, если хотим.
Поэтому функция, которую мы кодируем, должна делать следующее:
- Удалить пробелы и преобразовать в верхний регистр (или нижний регистр).
- Проверьте, является ли ввод исключением, если он должен возвращать действительный
- Проверьте, равна ли длина входа 4 < длина < 8.
- Проверьте правильность почтового индекса.
Последняя часть хитрая, но мы разберем ее на 3 части по длине для некоторого обзора:
- Длина = 7: AA9A9AA и AA999AA
- Длина = 6: AA99AA, A9A9AA и A999AA
- Длина = 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})
Я создал лондонские приложения только на основе почтовых индексов, используя почтовые индексы, которые я получил ЗДЕСЬ. Но, честно говоря, даже с лондонскими почтовыми индексами вам нужно гораздо больше памяти, чем необходимо. Конечно, идея тривиальна.
Сохраните почтовые индексы, возьмите пользовательский ввод или что-то еще, и посмотрите, если вы получите совпадение. Но вы усложняете решение гораздо больше, чем думаете. Я ДОЛЖЕН использовать реальные почтовые индексы для достижения того, чего я хотел, но для простых целей проверки, таких же сложных, как "поддержание" регулярного выражения, хранение десятков тысяч или сотен тысяч (если не больше) и проверка большей или меньшей степени в режиме реального времени. это гораздо более сложная задача.
Если мини-распределенный сервис звучит как более эффективное решение, чем регулярное выражение, сделайте это, но я уверен, что это не так. Если вам не нужны геопространственные запросы ваших собственных данных к британским почтовым индексам или другим подобным вещам, я сомневаюсь, что хранение БД - это реальное решение. Просто мои 2 цента.
Обновить
Согласно этому индексу, в Великобритании насчитывается 1 758 417 почтовых индексов. Я могу сказать вам, что я использую несколько кластеров Mongo (Amazon EC2 High Memory Instances) для предоставления надежных услуг только в Лондоне (индексирование только лондонских почтовых индексов), и это довольно дорого, даже с базовым хранилищем.
Следует признать, что приложение выполняет геопространственные запросы средней сложности, но одни только требования к хранилищу очень дороги и требуют больших затрат.
Итог, просто придерживайтесь регулярных выражений и покончите с этим через две минуты.
Я думаю, что RegEX, хотя скучный, вероятно, будет лучшим решением, если все, что вы хотите сделать, это проверить, если что-то может быть действительным почтовым индексом Великобритании.
Если вам нужны абсолютные данные, подумайте об использовании инициативы Ordnance Survey OpenData " Набор данных Code-Point® Open", которая представляет собой CSV большого количества точек данных в Великобритании (так что я полагаю, не в Северной Ирландии), одной из которых является почтовый индекс. Помните, что размер файла составляет 20 МБ, поэтому вам, возможно, придется преобразовать его в более управляемый формат.
Я сейчас смотрю на ссылку Почтовые индексы в Википедии.
http://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom
В разделе "Проверка" перечислены шесть форматов с комбинацией букв и цифр. Тогда есть больше информации в примечаниях ниже этого. Первое, что я бы попробовал - это грамматика типа BNF с помощью такого инструмента, как GoldParserBuilder. Вы можете описать основные форматы в более удобочитаемом формате, с автоматически генерируемыми эффективными синтаксическим анализатором и лексером. В прошлом я успешно использовал такие инструменты, чтобы избежать написания огромных, безобразных регулярных выражений.
С этого момента программа имеет правильно отформатированный почтовый индекс известного типа. На этом этапе конкретные цифры или буквы могут что-то нарушать. У каждого типа почтового индекса может быть запрограммирована функция для поиска нарушений этого конкретного типа. Конечный продукт будет состоять из автоматически сгенерированного парсера, который передает неподтвержденные, но структурированные / идентифицированные почтовые индексы в выделенную функцию проверки. Затем вы можете рефакторинг или оптимизацию оттуда.
(Вы также можете использовать саму грамматику для принудительного применения или запрета определенных литералов и комбинаций. Что бы ни было для вас более читабельным или понятным. Разные люди тяготеют к разным целям этих вещей.)
Вот страница, освещающая преимущества системы разбора GOLD. Вы можете использовать любую, какую захотите: я просто рекламирую эту, потому что она хорошо справляется со своей работой и постоянно совершенствуется в течение многих лет. http://www.goldparser.org/about/why-use-gold.htm
Регулярные выражения трудно отлаживать, их сложно перенести из одного варианта регулярного выражения в другой ("тихие" ошибки ")", и их сложно обновить.
Это верно для большинства регулярных выражений, но почему бы вам просто не разделить его на несколько частей? Вы можете легко разделить его на шесть частей для шести различных общих правил и, возможно, даже больше, если принять во внимание все особые случаи.
Создание хорошо закомментированного метода из 20 строк с простыми регулярными выражениями легко отлаживать (по одному простому регулярному выражению на строку), а также легко обновлять. Проблема переноса та же, но, с другой стороны, вам не нужно использовать какую-то причудливую грамматическую библиотеку.
+1 за комментарии "Зачем заботиться". Мне приходилось использовать "официальное" регулярное выражение в различных проектах, и хотя я никогда не пытался его разбить, оно работает и выполняет свою работу. Я использовал его с кодом Java и PHP без необходимости конвертировать его в форматы регулярных выражений.
Есть ли причина, по которой вам пришлось бы отлаживать или ломать его?
Кстати, правило регулярных выражений раньше можно было найти в Википедии, но, похоже, оно исчезло.
Изменить: Что касается дебатов пробела / пробела, почтовый индекс должен быть действительным с пробелом или без него. Поскольку последняя часть почтового индекса (после пробела) ВСЕГДА состоит из трех цифр, можно вставить пробел вручную, что затем позволит вам выполнить его через правило регулярных выражений.
Являются ли сторонние услуги опцией?
http://www.postcodeanywhere.co.uk/address-validation/
База данных GeoNames:
Возьмите список действительных почтовых индексов и проверьте, есть ли в нем введенный.