БлогNot. PHP: извлекаем из текста номера сотовых с разными разделителями

PHP: извлекаем из текста номера сотовых с разными разделителями

Номера сотовых - хороший пример данных, которые люди записывают "разными способами": 89530011204 или +7 953 001 12 04 или +7 953-001-1204 и т.д. обозначают совершенно одно и то же, хотя для машинного анализа это далеко не очевидно.

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

А можно подойти к задаче по-другому, применив немного программирования.

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

Задачи "вытащить" номер из строки вроде "8 953 ноль ноль 1 двенадцать 04" мы не ставим, хотя её можно решать на том же принципе, просто усложнив метод phone_filter и увеличив константу TEST_LIMIT.

Общий подход, получившийся у меня после часа экспериментов, представляется таким:

1. Получить массив лексем (слов) из исходной строки $data:

1.1. Убрать лишние разделители через preg_replace;

1.2. Получить массив лексем через preg_split;

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

2. Искомым номером может быть как всё очередное слово (89530011204), так и несколько подряд идущих слов (+7 953 001 12 04), при этом между группами цифр могут быть всякие посторонние символы (+7 953, 001,1202). Поэтому мы разобьём задачу на 2 этапа:

2.1. Проверить, не соответствует ли сама лексема (фильтрованная от лишних символов) нужному шаблону, если да, занести её в массив результатов.

2.2. Если нет, прицепить несколько следующих лексем, фильтруя получающуюся строку и проверяя её на соответствие шаблону. Остановиться, если длина такой "составной" и отфильтрованной лексемы превысила некий разумный лимит, в нашем случае, значение 12.

Требуются 2 дополнительных метода:

  • phone_filter - фильтрует лексему или цепочку соединённых соседних лексем, убирая символы, которых не должно быть в целевой строке. В нашем случае мы убираем дефисы и всё, что не является цифрой или "плюсом" (шаблон [^\d\+]);
  • is_phone - проверяет отфильтрованную строку на жёсткое соответствие нужному шаблону, в нашем случае ^(\+7|8)(\d{10})$, то есть, номер распознаётся в виде +79530011204.

Зачем два метода, а не один? А чтобы проверять, не превышена ли максимально возможная длина целевой строки для уже отфильтрованных данных. Если сунуть функционал phone_filter в is_phone, можно потерять "замусоренные" цепочки нужных символов.

Алгоритм не оптимален по производительности, но и не слишком страшен для разовой практической цели, для которой он мне понадобился.

Вот полный исходник на PHP+HTML, файл сохранять в Юникоде (UTF-8):

<!DOCTYPE html>
<html dir="ltr" lang="ru-RU">
<head> <meta charset="utf-8">
 <title>Номера сотовых из текста</title>
</head><body>
<?php
 $data = "Я текст+++, в котором могут быть номера 8 953 001 12 01
и такие тоже +7 953, 001,1202 не номер 234564 89139992203
89530011204 и еще такие 
 + 7 953-001-12-05 номер может даже  +7 953-001-12-
 06 переноситься на другую строку, 
 +7   953-001-1207, содержать лищние пробелы,
и так далее +7 953-001-1208
 в общем, +79530011209 , будем считать 
что они в любой записи 8-9530011210
и такой тоже 89530 011 211! +7-961-222-33-12";
 define ('TEST_LIMIT','12'); //лимит проверяемой длины строк
 
 $data = preg_replace ("/\s+/u"," ",$data); //убрали лишние разделители
 $tokens = preg_split("/\s/u",$data); //получили массив лексем
 $tokens = array_filter ($tokens, function ($item) {return !empty($item);} );
  //отфильтровали пустые лексемы
 $len = count($tokens); $result = array (); $i = 0;
 for ($i=0; $i<$len; $i++) { //цикл по лексемам
  if (is_phone(phone_filter($tokens[$i]))) 
   $result[] = $tokens[$i]; //сама фильтрованная лексема есть номер
  else { //или пробуем сливать лексему с несколькими последующими
   $test = $tokens[$i]; $j = $i+1;
   while (1) {
	$test .= $tokens[$j]; 
	$test = phone_filter ($test);
	if (is_phone($test)) { $result[] = $test; $i=$j; break; }
	else if ($j>=$len or strlen($test)>TEST_LIMIT) break;
	$j++;
   } 
  } 
 }
 
 print_r($result); //вывод результатов
 
 function phone_filter ($s) { //фильтр символов проверяемой лексемы
  return preg_replace (array("/\-/u","/[^\d\+]/u"),"",$s);
 }
 
 function is_phone ($s) { //проверка лексемы на соответствие шаблону
  return preg_match("/^(\+7|8)(\d{10})$/u",$s) ? 1 : 0;
 }
?>
</body></html>

Результат прогона:

Array ( [0] => 89530011201 [1] => +79530011202 [2] => 89139992203 [3] => 89530011204 [4] => +79530011205 [5] => +79530011206 [6] => +79530011207 [7] => +79530011208 [8] => +79530011209 [9] => 89530011210 [10] => 89530011211 [11] => +7-961-222-33-12 )

28.10.2016, 13:24 [5709 просмотров]


теги: учебное программирование php алгоритм мобильник

К этой статье пока нет комментариев, Ваш будет первым