Я недавно опубликовал ответ на этот вопрос на британских почтовых индексов для языка R . Я обнаружил, что шаблон регулярных выражений правительства Великобритании неверен и не может правильно проверить некоторые почтовые индексы. К сожалению, многие ответы здесь основаны на этой неправильной схеме.
Я изложу некоторые из этих вопросов ниже и предоставлю исправленное регулярное выражение, которое действительно работает.
Заметка
Мой ответ (и регулярные выражения в целом):
- Проверяет только форматы почтовых индексов .
- Не гарантирует, что почтовый индекс законно существует .
- Для этого используйте соответствующий API! Смотрите ответ Бена для получения дополнительной информации.
Если вас не интересует плохое регулярное выражение, и вы просто хотите пропустить ответ, прокрутите вниз до раздела « Ответ ».
Плохое регулярное выражение
Регулярные выражения в этом разделе не должны использоваться.
Это ошибочное регулярное выражение, которое правительство Великобритании предоставило разработчикам (не уверен, как долго будет действовать эта ссылка, но вы можете увидеть это в их документации Bulk Data Transfer ):
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$
Проблемы
Проблема 1 - Копировать / Вставить
Смотрите регулярное выражение в использовании здесь .
Как, вероятно, делают многие разработчики, они копируют / вставляют код (особенно регулярные выражения) и вставляют их, ожидая, что они будут работать. Хотя это хорошо в теории, в данном конкретном случае это терпит неудачу, потому что копирование / вставка из этого документа фактически превращает один из символов (пробел) в символ новой строки, как показано ниже:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))
[0-9][A-Za-z]{2})$
Первое, что сделает большинство разработчиков, это просто удалите новую строку, не задумываясь. Теперь регулярное выражение не будет сопоставлять почтовые индексы с пробелами в них (кроме GIR 0AA
почтового индекса).
Чтобы решить эту проблему, символ новой строки должен быть заменен символом пробела:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Проблема 2 - Границы
Смотрите регулярное выражение в использовании здесь .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^^ ^ ^ ^^
Регулярное выражение почтового индекса неправильно привязывает регулярное выражение. Любой, кто использует это регулярное выражение для проверки почтовых индексов, может быть удивлен, если получится такое значение fooA11 1AA
. Это потому, что они привязали начало первого и конец второго (независимо друг от друга), как указано в регулярном выражении выше.
Это означает, что ^
(утверждает позицию в начале строки) работает только с первым параметром ([Gg][Ii][Rr] 0[Aa]{2})
, поэтому второй параметр будет проверять все строки, заканчивающиеся почтовым индексом (независимо от того, что предшествует ранее).
Аналогично, первая опция не привязана к концу строки $
, поэтому GIR 0AAfoo
также принимается.
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z]))))[0-9][A-Za-z]{2})$
Чтобы решить эту проблему, оба параметра должны быть заключены в другую группу (или группу без захвата), а вокруг них должны быть размещены якоря:
^(([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2}))$
^^ ^^
Проблема 3 - Неправильный набор символов
Смотрите регулярное выражение в использовании здесь .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^^
Регулярное выражение отсутствует -
здесь, чтобы указать диапазон символов. Как есть, если почтовый индекс имеет формат ANA NAA
(где A
представляет букву и N
представляет число) и начинается с чего-либо, кроме A
или Z
, он потерпит неудачу.
Это означает, что это будет соответствовать A1A 1AA
и Z1A 1AA
, но не B1A 1AA
.
Чтобы решить эту проблему, символ -
должен быть помещен между A
и Z
в соответствующем наборе символов:
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Проблема 4 - Неправильный дополнительный набор символов
Смотрите регулярное выражение в использовании здесь .
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
^
Я клянусь, что они даже не проверяли эту вещь, прежде чем публиковать ее в Интернете. Они сделали неправильный набор символов необязательным. Они сделали [0-9]
вариант в четвертом подопции варианта 2 (группа 9). Это позволяет регулярному выражению соответствовать неправильно отформатированным почтовым индексам AAA 1AA
.
Чтобы решить эту проблему, сделайте следующий класс символов необязательным (и впоследствии сделайте так, чтобы набор [0-9]
совпадал ровно один раз):
^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?)))) [0-9][A-Za-z]{2})$
^
Проблема 5 - Производительность
Производительность на этом регулярном выражении крайне плохая. Во-первых, они поместили наименее вероятный вариант шаблона для соответствия GIR 0AA
в начале. Сколько пользователей будет иметь этот почтовый индекс по сравнению с любым другим почтовым индексом; наверное никогда? Это означает, что каждый раз, когда используется регулярное выражение, он должен сначала исчерпать эту опцию, прежде чем перейти к следующей. Чтобы увидеть, как это влияет на производительность, проверьте количество шагов, которые исходное регулярное выражение (35) предприняло по отношению к тому же регулярному выражению после переключения опций (22).
Вторая проблема с производительностью связана с тем, как структурировано все регулярное выражение. Там нет смысла возвращаться к каждому варианту, если один не удастся. Структура текущего регулярного выражения может быть значительно упрощена. Я исправляю это в разделе « Ответ ».
Задача 6 - Пространства
Смотрите регулярное выражение в использовании здесь
Это само по себе не может считаться проблемой , но вызывает беспокойство у большинства разработчиков. Пробелы в регулярном выражении не являются обязательными, это означает, что пользователи, вводящие свои почтовые индексы, должны поместить пробел в почтовый индекс. Это легко исправить, просто добавив ?
после пробелов сделать их необязательными. Смотрите раздел Ответ для исправления.
Ответ
1. Исправление регулярного выражения правительства Великобритании
Исправление всех проблем, описанных в разделе « Проблемы » и упрощение шаблона, дает следующий, более короткий и более лаконичный шаблон. Мы также можем удалить большинство групп, так как мы проверяем почтовый индекс в целом (не отдельные части):
Смотрите регулярное выражение в использовании здесь
^([A-Za-z][A-Ha-hJ-Yj-y]?[0-9][A-Za-z0-9]? ?[0-9][A-Za-z]{2}|[Gg][Ii][Rr] ?0[Aa]{2})$
Это может быть дополнительно сокращено путем удаления всех диапазонов из одного из регистров (верхнего или нижнего регистра) и использования флага без учета регистра. Примечание . Некоторые языки не имеют такового, поэтому используйте более длинный выше. Каждый язык реализует флаг нечувствительности к регистру по-своему.
Смотрите регулярное выражение в использовании здесь .
^([A-Z][A-HJ-Y]?[0-9][A-Z0-9]? ?[0-9][A-Z]{2}|GIR ?0A{2})$
Короче снова заменить [0-9]
на \d
(если ваш движок регулярных выражений поддерживает это):
Смотрите регулярное выражение в использовании здесь .
^([A-Z][A-HJ-Y]?\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
2. Упрощенные шаблоны
Без указания конкретных буквенных символов можно использовать следующее (имейте в виду, что здесь были использованы упрощения, приведенные в разделе 1. Исправление регулярного выражения правительства Великобритании ):
Смотрите регулярное выражение в использовании здесь .
^([A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}|GIR ?0A{2})$
И даже дальше, если вас не волнует особый случай GIR 0AA
:
^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$
3. Сложные паттерны
Я не рекомендовал бы чрезмерную проверку почтового индекса, поскольку новые районы, районы и районы могут появиться в любой момент времени. Что я буду предлагать делать потенциально , так это добавлена поддержка для крайних случаев. Существуют некоторые особые случаи, которые описаны в этой статье Википедии .
Вот сложные регулярные выражения, которые включают в себя подразделы 3. (3.1, 3.2, 3.3).
Относительно моделей в 1. Исправление регулярного выражения правительства Великобритании :
Смотрите регулярное выражение в использовании здесь
^(([A-Z][A-HJ-Y]?\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$
И по отношению к 2. Упрощенные паттерны :
Смотрите регулярное выражение в использовании здесь
^(([A-Z]{1,2}\d[A-Z\d]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?\d[A-Z]{2}|BFPO ?\d{1,4}|(KY\d|MSR|VG|AI)[ -]?\d{4}|[A-Z]{2} ?\d{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$
3.1 Британские заморские территории
Статья в Википедии в настоящее время утверждает (некоторые форматы немного упрощены):
AI-1111
: Ангилья
ASCN 1ZZ
: Остров Вознесения
STHL 1ZZ
: Святой Елены
TDCU 1ZZ
Тристан-да-Кунья
BBND 1ZZ
: Британская территория Индийского океана
BIQQ 1ZZ
: Британская Антарктическая Территория
FIQQ 1ZZ
: Фолклендские острова
GX11 1ZZ
: Гибралтар
PCRN 1ZZ
: Острова Питкэрн
SIQQ 1ZZ
: Южная Георгия и Южные Сандвичевы острова
TKCA 1ZZ
: Острова Теркс и Кайкос
BFPO 11
: Акротири и Декелия
ZZ 11
& GE CX
: Бермудские острова (согласно этому документу )
KY1-1111
: Каймановы острова (согласно этому документу )
VG1111
: Британские Виргинские острова (согласно этому документу )
MSR 1111
: Монтсеррат (согласно этому документу )
Всеобъемлющее регулярное выражение для соответствия только британским заморским территориям может выглядеть так:
Смотрите регулярное выражение в использовании здесь .
^((ASCN|STHL|TDCU|BBND|[BFS]IQQ|GX\d{2}|PCRN|TKCA) ?\d[A-Z]{2}|(KY\d|MSR|VG|AI)[ -]?\d{4}|(BFPO|[A-Z]{2}) ?\d{2}|GE ?CX)$
3.2 Почтовое отделение британских войск
Хотя они были недавно изменены, чтобы лучше соответствовать британской системе почтовых индексов BF#
(где #
представляет число), они считаются необязательными альтернативными почтовыми индексами . Эти почтовые индексы следуют (ed) формат BFPO
, за которым следуют 1-4 цифры:
Смотрите регулярное выражение в использовании здесь
^BFPO ?\d{1,4}$
3.3 Санта?
Есть еще один особый случай с Сантой (как уже упоминалось в других ответах): SAN TA1
это действительный почтовый индекс. Регулярное выражение для этого очень просто:
^SAN ?TA1$