Я недавно опублікував відповідь на це питання на британських поштових індексів для мови 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})
, тому другий варіант підтвердить будь-які рядки, що закінчуються в поштовому індексі (незалежно від того, що відбувається раніше).
Так само перший варіант не прив’язаний до кінця рядка $
, тому 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. Виправлення Regex уряду Великобританії також було застосовано тут):
Дивіться регекс тут .
^([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#
(де #
представляється число), вони вважаються необов'язковими альтернативними поштовими індексами . Ці поштові індекси дотримуються (редагують) формат, з BFPO
наступними 1-4 цифрами:
Дивіться регекс тут
^BFPO ?\d{1,4}$
3.3 Санта?
Є ще один особливий випадок із Дідом (як згадується в інших відповідях): SAN TA1
це дійсний поштовий індекс. Зворотний вираз для цього дуже просто:
^SAN ?TA1$