RegEx: Проверка и форматирование североамериканских телефонных номеров

Вам нужно определить, ввел ли пользователь североамериканский телефонный номер, включая код города, в общепринятом формате. К таким форматам относятся 1234567890, 123-456-7890, 123.456.7890, 123 456 7890, (123) 456 7890 и все соответствующие комбинации. Если телефонный номер действителен, необходимо преобразовать его в стандартный формат (123) 456-7890, чтобы записи телефонных номеров были единообразными.

С помощью регулярного выражения можно легко проверить, ввел ли пользователь что-то, похожее на действительный номер телефона. Используя группы захвата для запоминания каждого набора цифр, то же самое регулярное выражение можно использовать для замены текста темы именно в том формате, который вам нужен.

Решение

Замена

Пример на C#

Пример JavaScript

Обсуждение

Это регулярное выражение соответствует трем группам цифр. Первая группа может быть заключена в круглые скобки, а за первыми двумя группами может следовать один из трех разделителей (дефис, точка или пробел). Следующая схема разбивает регулярное выражение на отдельные части, опуская лишние группы цифр:

Давайте рассмотрим каждую из этих частей подробнее.

Знаки '^' и '$' в начале и конце регулярного выражения - это особый вид метасимвола, называемый якорем или утверждением. Вместо того чтобы соответствовать тексту, утверждения соответствуют позиции в тексте. В частности, '^' совпадает с началом текста, а '$' - с его концом. Это гарантирует, что regex номера телефона не будет соответствовать более длинному тексту, например 123-456-78901.

Как мы уже неоднократно видели, круглые скобки являются специальными символами в регулярных выражениях, но в данном случае мы хотим, чтобы пользователь мог ввести круглые скобки и чтобы наш регекс их распознал. Это хрестоматийный пример, когда нам нужен обратный слеш для экранирования специального символа, чтобы регулярное выражение воспринимало его как литеральный ввод. Таким образом, последовательности '\(' и '\)', которые окружают первую группу цифр, соответствуют буквальным символам скобок. После обоих символов следует вопросительный знак, что делает их необязательными. Подробнее о вопросительном знаке мы расскажем после обсуждения других типов лексем в этом регулярном выражении.

Круглые скобки, которые появляются без обратных слэшей, являются группами захвата и используются для запоминания значений, сопоставленных внутри них, чтобы впоследствии можно было вызвать сопоставленный текст. В данном случае обратные ссылки на захваченные значения используются в тексте замены, чтобы мы могли легко переформатировать номер телефона при необходимости.

Два других типа лексем, используемых в этом регулярном выражении, - это символьные классы и квантификаторы. Классы символов позволяют подобрать любой из набора символов. '[0-9]' - это класс символов, который соответствует любой цифре. Все варианты регулярных выражений, рассматриваемые в этой книге, включают в себя сокращенный класс символов '\d', который также соответствует цифре, но в некоторых вариантах '\d' соответствует цифре из набора символов любого языка или скрипта, а это не то, что нам нужно в данном случае. Более подробную информацию о '\d' см. в рецепте 2.3.

'[-. ]' - это еще один класс символов, который допускает любой из трех разделителей. Важно, чтобы дефис был первым или последним в этом классе символов, потому что если бы он появился между другими символами, то создал бы диапазон, как в случае с '[0-9]'. Другой способ гарантировать, что дефис внутри символьного класса соответствует буквальной версии самого себя, - это экранировать его с помощью обратного слеша. Таким образом, '[.\-●]' эквивалентен. Дефис '●' представляет собой буквальный символ пробела.

Наконец, квантификаторы позволяют многократно повторить совпадение лексем или групп. '{3}' - это квантификатор, который заставляет предыдущий элемент совпадать ровно три раза. Таким образом, регулярное выражение '[0-9]{3}' эквивалентно '[0-9][0-9][0-9]', но оно короче и, надеюсь, легче читается. Вопросительный знак (упоминавшийся ранее) - это квантификатор, который заставляет предшествующий ему элемент совпадать ноль или один раз. Он также может быть записан как '{0,1}'. Любой квантификатор, позволяющий чему-либо совпасть с элементом ноль раз, фактически делает этот элемент необязательным. Поскольку после каждого разделителя используется вопросительный знак, цифры телефонного номера могут идти вместе.

Обратите внимание, что хотя этот рецепт заявляет о работе с североамериканскими телефонными номерами, на самом деле он предназначен для работы с номерами Североамериканского плана нумерации (NANP). NANP - это план телефонной нумерации для стран, имеющих общий код страны «1». Сюда входят Соединенные Штаты и их территории, Канада, Бермудские острова и 17 стран Карибского бассейна. В него не входят Мексика и страны Центральной Америки.

Варианты

Исключение недействительных телефонных номеров

До сих пор регулярное выражение соответствовало любому 10-значному номеру. Если вы хотите ограничить совпадения действительными телефонными номерами в соответствии с Североамериканским планом нумерации, то вот основные правила:

  • Коды городов начинаются с цифр 2-9, за ними следуют 0-8, а затем любая третья цифра.
  • Вторая группа из трех цифр, известная как код центрального офиса или станции, начинается с цифры 2-9, за которой следуют любые две цифры.
  • Последние четыре цифры, известные как код станции, не имеют ограничений.

Помимо только что перечисленных основных правил, существует множество зарезервированных, не назначенных и ограниченных телефонных номеров. Если у вас нет особых потребностей, требующих отфильтровать как можно больше телефонных номеров, не пытайтесь избавиться от неиспользуемых номеров. Новые телефонные коды, соответствующие перечисленным ранее правилам, появляются регулярно, и даже если телефонный номер действителен, это не обязательно означает, что он был выдан или активно используется.

Поиск телефонных номеров в документах

Два простых изменения позволяют использовать предыдущие регулярные выражения для поиска телефонных номеров в длинном тексте:

Утверждения '^' и '$', которые привязывали регулярное выражение к началу и концу текста, были удалены. Вместо них добавлены маркеры границ слов ('\b'), чтобы убедиться, что найденный текст стоит сам по себе и не является частью более длинного числа или слова.

Подобно '^' и '$', '\b' - это утверждение, которое соответствует позиции, а не какому-либо тексту. В частности, '\b' соответствует позиции между символом слова и символом, не являющимся словом, либо началом или концом текста. Буквы, цифры и подчеркивание считаются символами слова (см. Рецепт 2.6).

Обратите внимание, что первый маркер границы слова появляется после необязательной открывающей скобки. Это важно, поскольку между двумя несловарными символами, такими как открывающая скобка и предшествующий пробел, нет границы слова, которую нужно сопоставить. Первая граница слова важна только при сопоставлении номера без скобок, поскольку граница слова всегда совпадает между открывающей скобкой и первой цифрой телефонного номера.

Разрешить ведущую «1»

Вы можете разрешить необязательный ведущий «1» для кода страны (который охватывает регион Североамериканского плана нумерации) с помощью дополнения, показанного в следующем regex:

В дополнение к форматам телефонных номеров, показанным ранее, это регулярное выражение также будет соответствовать таким строкам, как +1 (123) 456-7890 и 1-123-456-7890. В нем используется группа неперехвата, которая записывается как '(?:⋯)'. Когда вопросительный знак следует за левой скобкой без раскрытия, как здесь, он не является квантификатором, а помогает определить тип группировки. Стандартные группы захвата требуют, чтобы механизм регулярных выражений отслеживал обратные ссылки, поэтому эффективнее использовать группы без захвата, когда текст, сопоставленный группе, не нуждается в последующих ссылках. Еще одна причина использовать здесь не захватывающую группу - это возможность продолжать использовать ту же строку замены, что и в предыдущих примерах. Если бы мы добавили группу захвата, нам пришлось бы изменить $1 на $2 (и так далее) в тексте замены, показанном ранее в этом рецепте.

Полное дополнение к этой версии regex - '(?:\+?1[-. ]?)?'. Перед «1» в этом шаблоне стоит необязательный знак плюс, а за ним по желанию следует один из трех разделителей (дефис, точка или пробел). Вся добавляемая группа не захвата также необязательна, но поскольку «1» требуется внутри группы, предшествующий знак плюс и разделитель не допускаются, если нет ведущей «1».

Разрешить семизначные телефонные номера

Чтобы разрешить совпадение телефонных номеров, в которых отсутствует код города, заключите первую группу цифр вместе с окружающими скобками и следующим разделителем в необязательную, не захватывающую группу:

Поскольку код города больше не требуется как часть соответствия, простая замена любого соответствия на «($1) $2-$3» может привести к чему-то вроде () 123-4567 с пустым набором круглых скобок. Чтобы обойти эту проблему, добавьте код за пределами regex, который проверяет, совпала ли группа 1 с каким-либо текстом, и соответствующим образом измените текст замены.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий