Pull to refresh

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

Reading time8 min
Views2.2K
В первой части статьи разработчики библиотеки MSLibrary for iOS рассказали об особенностях структуры телефонных номеров с точки зрения международных стандартов, опубликованных в документе RFC 3966 , рассмотрели Международную структуру телефонных номеров, корпоративные WEB стандарты набора телефонного номера, их взаимодействие между собой и то, как ведут себя пользователи.
Как уже было сказано, захват и верификация это — разные задачи, но решаются они схожими методами, различающимися в основном применяемыми в них регулярными выражениями. Во второй части статьи речь пойдет собственно о регулярных выражениях.

Верификация телефонных номеров

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

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

Рассмотрим оба варианта, но сначала общие соображения.

Валидация строки телефонного номера, чтобы он был адекватно обработан iOS, сводится к нескольким условиям:
1. общая структура номера должна соответствовать документу RFC 3966

telephone-uri = global-number-digits [extension]
рис. 1

2. телефонный номер должен всегда начинаться и заканчиваться с цифры

3. структура global-number-digits должна соответствовать документу RFC 3966 с учетом п.2 ( см. первую часть статьи )
	global-number-digits	= "+" * DIGIT *phonedigit DIGIT 

4. количество цифровых знаков в global-number-digits должно находиться в диапазоне 11-13 цифр (это условие следует из Международной структуры телефонных номеров)

5. структура необязательного элемента — добавочного номера [extension] должна отличаться от предложенной в документе RFC 3966
	extension		= (";" | *",") 1*(DIGIT *phonedigit DIGIT)

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

telephone-uri = "+" DIGIT (9-11)*phonedigit DIGIT [(";" | *",") DIGIT *phonedigit DIGIT]
рис. 2

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

Для наглядности начнем с регулярных выражений для каждой из частей структуры, изображенной на рис. 2.

DIGIT
	REGEX			[0-9]
	REGEX_OBJC_STRING	@"\\d"

phonedigit
	REGEX			[0-9]|[-\.\(\)]
	REGEX_OBJC_STRING	@"\\d|[-\\.\\(\\)]"

Приступим к конструированию регулярного выражения для первого варианта. Выберем одно, самое простое, написание валидного для iOS систем телефонного номера, например
	+14089961010;1234

Регулярное выражение, описывающее структуру этого телефонного номера, выглядит следующим образом:
	REGEX			^((?:\+?[0-9]{11,13})(?:(;|,+)[0-9]+)?)$

Разберем это выражение:
	^(			# начало строки
	(?:			# начало блока global-number-digits
	\+?			# знак "+" (возможно использование не более одного раза)
	[0-9]			# сегмент DIGIT блока global-number-digits
	{11,13}			# указатель на то, что сегмент DIGIT возможен от 11 до 13 раз
	)			# окончание блока global-number-digits
	(?:			# начало блока extension
	(;|,+)			# сепаратор, указывающие на начало добавочного номера
	[0-9]+			# сегмент DIGIT блока extension
	)?			# окончание блока extension
	)$			# окончание строки


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

	REGEX_OBJC_STRING	@"^((?:\\+?\\d{11,13})(?:(;|,+)\\d+)?)$"

Для второго варианта, валидного для iOS систем, телефонного номера, полностью удовлетворяющего структуре, изображенной на рис. 2, регулярное выражение, выглядит следующим образом:
	REGEX			^((?:\+?[0-9]([-.\(\)]?[0-9]){10,12})(?:(;|,+)([0-9]([-.\(\)]?[0-9])?)+)?)$

Разберем это выражение:
	^(			# начало строки
	(?:			# начало блока global-number-digits
	\+?			# знак "+" (возможно использование не более одного раза)
	[0-9]			# первый сегмент DIGIT блока global-number-digits
	(			# начало сегмента phonedigit блока global-number-digits
	[-.\(\)]?		# визуальный сепаратор (возможно использование не более одного раза в данном сегменте)
	[0-9]			# второй сегмент DIGIT блока global-number-digits
	)			# окончание сегмента phonedigit блока global-number-digits
	{10,12}			# указатель на то, что сегмент phonedigit возможен от 10 до 12 раз
	)			# окончание блока global-number-digits
	(?:			# начало блока extension
	(;|,+)			# сепаратор, указывающие на начало добавочного номера
	(			# начало цифровой части блока extension
	[0-9]			# первый сегмент DIGIT блока extension
	(			# начало сегмента phonedigit блока extension
	[-.\(\)]?		# визуальный сепаратор (возможно использование не более одного раза в данном сегменте)
	[0-9]			# второй сегмент DIGIT блока extension
	)?			# окончание сегмента phonedigit блока extension
	)+			# окончание цифровой части блока extension
	)?			# окончание блока extension
	)$			# окончание строки


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

	REGEX_OBJC_STRING	@"^((?:\\+?\\d([-.\\(\\)]?\\d){10,12})(?:(;|,+)(\\d([-.\\(\\)]?\\d)?)+)?)$"


Захват телефонных номеров

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

2. могут содержаться инородные элементы

3. валидные сепараторы могут быть подменены на невалидными

На втором и третьем пунктах следует остановиться подробнее. Попробуем разобраться в весьма расплывчатых понятиях «инородные» и «невалидные» элементы.

В качестве визуальных сепараторов, кроме рассмотренных выше, при ручном наборе часто используют пробел (whitespace). Пробел может быть использован и как невалидный сепаратор и как инородный элемент, в случае, если он стоит перед или после сепаратора.
Для, обозначения начала добавочного номера, могут быть использованы следующие невалидные сепараторы:
	"x" | "ext" | ":" | "p" | "=" 


С учетом вышесказанного, регулярное выражение для phonedigit и сепаратора, обозначающего начало добавочного номера, примут следующий вид:
phonedigit
	REGEX			[0-9]|(\s?[-\.\(\)\s]\s?)
	REGEX_OBJC_STRING	@"[0-9]|(\\s?[-\\.\\(\\)\\s]\\s?)"

сепаратор добавочного номера
	REGEX			(x|ext|p|;|=|,+)
	REGEX_OBJC_STRING	@"(x|ext|p|;|=|,+)"


Регулярное выражение, удовлетворяющее поставленным условиям, выглядит следующим образом:
	REGEX			 ^(\s?(?:(\+?\s?[0-9]((\s?[-\.\(\)\s]\s?)?[0-9]){10,12}))(?:((x|ext|p|;|=|,+)(\s?[0-9]((\s?[-\.\(\)\s]\s?)?[0-9])?)+)?)$ 

Разберем это выражение:
	^(			# начало строки
	\s?			# пробел, допустимый в начале строки
	(?:(			# начало блока global-number-digits
	\+?			# знак "+" (возможно использование не более одного раза)
	\s?			# пробел, допустимый после знака "+"
	[0-9]			# первый сегмент DIGIT блока global-number-digits
	(			# начало сегмента phonedigit блока global-number-digits
	(\s?[-\.\(\)\s]\s?)?	# визуальный сепаратор (возможно использование не более одного раза в данном сегменте)
	[0-9]			# второй сегмент DIGIT блока global-number-digits
	)			# окончание сегмента phonedigit блока global-number-digits
	{10,12}			# указатель на то, что сегмент phonedigit возможен от 10 до 12 раз
	))			# окончание блока global-number-digits
	(?:(			# начало блока extension
	(x|ext|p|;|=|,+)	# сепаратор, указывающие на начало добавочного номера
	(			# начало цифровой части блока extension
	\s?			# пробел, допустимый в начале цифровой части блока extension
	[0-9]			# первый сегмент DIGIT блока extension
	(			# начало сегмента phonedigit блока extension
	(\s?[-\.\(\)\s]\s?)?	# визуальный сепаратор (возможно использование не более одного раза в данном сегменте)
	[0-9]			# второй сегмент DIGIT блока extension
	)?			# окончание сегмента phonedigit блока extension
	)+			# окончание цифровой части блока extension
	)?			# окончание блока extension
	)$			# окончание строки


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

	REGEX_OBJC_STRING	@"^(\\s?(?:(\\+?\\s?\\d((\\s?[-\\.\\(\\)\s]\\s?)?\\d){10,12}))(?:((x|ext|p|;|=|,+)(\\s?\\d((\\s?[-\\.\\(\\)\\s]\\s?)?\\d)?)+)?)$"

Для того, чтобы использовать захваченный таким образом телефонный номер, необходимо произвести его нормирование, то есть привести строку к валидному для использования в iOS виду. Описание технологии нормирования выходит за рамки тематики данной статьи. Можно сказать только, что в библиотеке MSLibrary for iOS эта задача решается применением одной единственной функции.

Подведем итоги

Задача верификации или валидации строки телефонного номера решается с помощью одного из следующих регулярный выражений:
	REGEX_OBJC_STRING	@"^((?:\\+?\\d{11,13})(?:(;|,+)\\d+)?)$"

	REGEX_OBJC_STRING	@"^((?:\\+?\\d([-.\\(\\)]?\\d){10,12})(?:(;|,+)(\\d([-.\\(\\)]?\\d)?)+)?)$"

Задача захвата строки, содержащей телефонный номер, может быть решена с помощью следующего регулярного выражения:
	REGEX_OBJC_STRING	@"^(\\s?(?:(\\+?\\s?\\d((\\s?[-\\.\\(\\)\s]\\s?)?\\d){10,12}))(?:((x|ext|p|;|=|,+)(\\s?\\d((\\s?[-\\.\\(\\)\\s]\\s?)?\\d)?)+)?)$"

Использовать полученные регулярные выражения при разработке iOS приложений можно с помощью методов класса NSRegularExpression .

В библиотеке MSLibrary for iOS для этого также имеются свои инструменты. К примеру, функция msfFRMreqMatchesInString

BOOL msfFRMreqMatchesInString(NSString *string, NSString *regularExpression, NSInteger reqNumberOfMatches)
рис. 3

Эта функция принимает значение YES или NO, зависимости от того, удовлетворяет ли строка «string» регулярному выражению «regularExpression» определенное количество раз — «reqNumberOfMatches». Как видите вопрос решается всего в одну строку кода.
Кроме того в библиотеке имеется несколько десятков тщательно подобранных регулярных выражений «на всякие случаи жизни».




Надеемся, что материал был для вас полезен, команда MSLibrary for iOS

Другие статьи:
Захват и верификация телефонных номеров с помощью регулярных выражений, для iOS и не только… Часть 1
Реализация множественного выбора условий с помощью битовых масок, для iOS и не только…
ПРОСТО: удаляем из строки ненужные символы, для iOS и не только…
Создание и компиляция кроссплатформенных (универсальных) библиотек в Xcode
Tags:
Hubs:
+2
Comments0

Articles