Я недавно отправил ответ на этот вопрос о почтовых индексах Великобритании для языка R . Я обнаружил, что шаблон регулярных выражений правительства Великобритании неверен и не может правильно проверить некоторые почтовые индексы. К сожалению, многие ответы здесь основаны на этой неправильной схеме.
Я кратко опишу некоторые из этих проблем ниже и приведу исправленное регулярное выражение, которое на самом деле работает.
Примечание
Мой ответ (и регулярные выражения в целом):
- проверяет только почтовый индекс форматы .
- Не гарантирует, что почтовый индекс законно существует .
- Для этого используйте соответствующий API! См. ответ Бена для получения дополнительной информации.
Если вас не волнует плохое регулярное выражение и вы просто хотите пропустить ответ, прокрутите вниз до раздела Ответ .
Плохое регулярное выражение
Регулярные выражения в этом разделе не должны использоваться.
Это ошибочное регулярное выражение, которое правительство Великобритании предоставило разработчикам (не знаю, как долго будет действовать эта ссылка, но вы можете увидеть это в их документации по массовой передаче данных ):
^([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})
, поэтому вторая опция будет проверять любые строки, которые end в почтовом индексе ( независимо от того, что происходит раньше).
Аналогично, первая опция не привязана к концу строки $
, поэтому 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 - Пробелы
Смотрите здесь регулярное выражение
Это, по сути, не может считаться проблемой 1164 *, но это вызывает беспокойство у большинства разработчиков. Пробелы в регулярном выражении не являются обязательными, это означает, что пользователи, вводящие свои почтовые индексы, должны поместить пробел в почтовый индекс. Это легко исправить, просто добавив ?
после пробелов, чтобы сделать их необязательными. См. Ответ раздел для исправления.
Ответ
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$