MSLibrary. Захват и верификация телефонных номеров с помощью регулярных выражений, для IOS и не только… Часть 1

в 10:03, , рубрики: Веб-разработка, разработка мобильных приложений, разработка под iOS, разработка приложений, Регулярные выражения

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

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

Любое решение, даже самое простое, хорошо лишь в том случае, если разработчик полностью осознает его работу и абсолютно в нем уверен.

По сути тема статьи разделяется на две уже в самом заголовке. Захват — это умение понять, что трестируемый набор цифр и знаков может быть телефонным номером. А верификация — это проверка соответствует ли этот набор неким, предварительно поставленным, условиям. В том, что эти задачи разные легко убедиться, посмотрев на приведенный пример:

	+ 1 (408) - 996 - 10 - 10 = 1234
	+14089961010;ext=1234

В первом случае некто попытался записать телефон в той манере в которой привык это делать, а во втором — телефон записан в соответствии с международным стандартом RFC 3966 . Проблема в том, что если мы попробуем использовать обе эти записи для набора телефонного номера, например в IOS приложении, то, к сожалению, ничего хорошего не получим. В первом случае система вообще ничего не поймет, а во втором, вместо добавочного номера «1234», система наберет совсем другие цифры (это очень убедительный эксперимент, можно попробовать. Код приведен ниже).

Простой код для телефонного звонка из IOS приложения.

   NSString *telString =@"tel:+14089961010;ext=1234";
   NSURL *urlString = [NSURL URLWithString: telString ];
   [[UIApplication sharedApplication] openURL:urlString];

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

Первым делом следует посмотреть, что же написано в регулирующем данный вопрос документе RFC 3966 . А написано там в сильно упрощенном виде следующее:

Упрощенная структура telephone-uri в соответствии с RFC 3966

telephone-uri                = "tel:" telephone-subscriber

  • telephone-subscriber	= global-number
  • global-number		= global-number-digits *par
  • par			= extension | isdn-subaddress | parameter
  • isdn-subaddress		= ";isub=" 1*uric
  • extension		= ";ext=" 1*phonedigit
  • global-number-digits	= "+" *phonedigit DIGIT *phonedigit
  • parameter		= ";" pname ["=" pvalue ]
  • pname			= 1*( alphanum | "-" )
  • pvalue			= 1*paramchar
  • paramchar		= param-unreserved | unreserved | pct-encoded
  • unreserved		= alphanum | mark
  • mark			= "-" | "_" | "." | "!" | "~" | "*"  "'" | "(" | ")"
  • param-unreserved	= "[" | "]" | "/" | ":" | "&" | "+" | "$"
  • phonedigit		= DIGIT | [ visual-separator ]
  • visual-separator	= "-" | "." | "(" | ")"
  • alphanum		= ALPHA | DIGIT
  • reserved		= ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
  • uric			= reserved | unreserved

где ALPHA и DIGIT, как следует из другого документа, — RFC 2396 :

  • DIGIT			= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
  • ALPHA			= lowalpha | upalpha
  • lowalpha		= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
  • upalpha			= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"

Схематично telephone-uri можно изобразить следующим образом:

telephone-uri = собственно телефонный номер [некое необязательное дополнение]

рис. 1

Из документа RFC 3966 следует, что:

  • собственно телефонный номер	= global-number-digits
  • некое необязательное дополнение	= extension | isdn-subaddress | parameter 

где:

  • extension			 — добавочный телефонный номер
  • isdn-subaddress	&#9	 — субадрес ISDN
  • parameter			 — некоторые другие необязательные параметры

Поскольку в реальной жизни при работе с мобильными приложениями, в частности с IOS приложениями, да и с большинством сайтов, встречается только добавочный телефонный номер, схема telephone-uri преобразится следующим образом:

telephone-uri = global-number-digits [extension]

рис. 2

или, подставив соответствующие значения для global-number-digits и extension:

telephone-uri = "+" *phonedigit DIGIT *phonedigit [";ext=" 1*phonedigit]

рис. 3

где phonedigit состоит из цифр "[0-9]" и ограниченного ассортимента визуальных разделителей

	"-" | "." | "(" | ")"

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

Международная структура телефонных номеров

В соответствии с существующей практикой, можно выделить следующие группы, в совокупности составляющие телефонный номер global-number-digits:

global-number-digits

  • "+"				— знак, указывающий, что после него расположен международный код страны
  • country_code			— одно-трехзначный код страны, например: 7, или 44 или 374
  • area_code			— трехзначный код региона, например: 800
  • exchange			— трехзначный номер станции, например: 555
  • subscriber_number		— четырехзначный номер абонента, например: 1234

extension

  • extension			— добавочный номер, например: 5678 (необязательный элемент)

В этом случае структура телефонного номера будет выглядеть следующим образом:

telephonNumber = "+" country_code [visual-separator] area_code [visual-separator] exchange [visual-separator] subscriber_number [visual-separator] [";ext=" extension]

рис. 4

Таким образом, если реализовать регулярное выражение, учитывающее стандарт RFC 3966 и Международную структуру телефонных номеров, иными словами, соответствующее схемам, изображенным на рис. 3 и рис. 4, следующие записи телефонных номеров будут вполне валидными:

	+14089961010;ext=1234
	+1(408)996-1010;ext=1234
	+1-408-996-10-10;ext=1234
	+1.408.996.1010;ext=1234

а следующие будут валидными только исходя из стандарта RFC 3966, поскольку визуальные сепараторы (visual-separator) находятся вне структуры, определяемой Международными стандартами телефонных номеров:

	+1(4089)96-1010;ext=1234
	+1-408996-10-10;ext=1234
	+1.408996.1010;ext=1234

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

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

Корпоративные WEB стандарты набора телефонного номера

Почему WEB? Потому, что в IOS, да и практически во всех других системах и, естественно, на сайтах, самый простой способ осуществить телефонный звонок — это использовать хорошо известрую html схему:

<a href="tel:1-408-996-1010">1-408-996-1010</a>

рис. 5

В начале статьи уже был приведен пример кода для реализации этой схемы на Objective C, однако, для стройности изложения, стоит повториться:

NSString *telString =@"tel:+14089961010";
NSURL *urlString = [NSURL URLWithString: telString ];
[[UIApplication sharedApplication] openURL:urlString];

рис. 6

Перейдем к корпоративным стандартам:

Что говорят по этому поводу специалисты компании Google?

Always supply the phone number using the international dialing format: the plus sign (+), country code, area code and number. While not absolutely necessary, it’s a good idea to separate each segment of the number with a hyphen (-) for easier reading and better auto-detection.

То есть необходимо ставить знак "+" перед кодом страны и «может быть хорошей идеей» разделять сегменты (группы) телефонного номера визуальными сепараторами в виде дефисов "-". Что в общем-то вполне согласуется с документом RFC 3966 и Международной структурой телефонных номеров, иными словами, соответствует схемам, изображенным на рис. 3 и рис. 4. Однако есть одно существенное НО. Визуальные сепараторы ограничены одним знаком, дефисом "-". Это конечно не означает, что браузер неадекватно отреагирует на скобки или точки в качестве визуальных сепараторов, однако это требует тщательной проверки, в документе Google гарантии даются только на дефис. Кроме того, в данном разделе инструкции ничего не говорится о формате добавочного номера. Поскольку статья посвящена в основном IOS приложениям, мы не будем углубляться в специфику работы гуголовского софта, интересующие могут поэкспериментировать и рассказать о результатах.

Компания Apple еще более немногословна, в разделе Phone Links , по поводу допустимого формата телефонного номера, мы находим такую фразу:

For more information about the tel URL scheme, see RFC 2806 and RFC 2396.

Формально все правильно, зачем повторяться, если есть международные стандарты? Но дело в том, что корпоративные стандарты компании Apple, также как и компании Google, соответствуют лишь части этих международных стандартов, которые, к слову, часто носят рекомендательный характер.

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

Что можно и чего нельзя использовать в телефонном номере для IOS приложения

Эксперименты показали, система адекватно реагирует на все четыре вида визуальных сепараторов, регламентированных в документе RFC 3966, а именно:

  • visual-separator = "-" | "." | "(" | ")"

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

	+14089961010
	+1(408)996-1010
	+1-408-996-10-10
	+1.408.996.1010
	+1(4089)96-1010
	+1-408996-10-10
	+1.408996.1010
	+1-4(0899(6-10-10
	+1.40)8996.10(10

По-другому дело обстоит с набором добавочного номера. Как уже говорилось в начале статьи, система неправильно реагирует на предлагаемый в документе RFC 3966 префикс добавочного кода ";ext=". Нативными, то есть естественными для системы являются два символа: ";" и ",".

В первом случае, когда система встречает в телефонном номере сепаратор ";", набор останавливается и на экране появляются цифры добавочного номера. При нажатии на них набор продолжается.

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

В случае появления сепаратора вида ";ext=", регламентируемого документом RFC 3966, происходит следующее: система принимает знак ";" за точку останова перед набором добавочного номера, а знаки «ext» интерпретирует как цифры с которых начинается добавочный номер.


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

Захват и верификация телефонных номеров с помощью регулярных выражений, для IOS и не только… Часть 2 — coming soon.

Автор: MSLibrary

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js