Конвертация номера телефона

Время на прочтение
13 мин

Количество просмотров 55K

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

Для начала приведу обзор найденных решений. Тем, кому это не интересно, рекомендую прокрутить ниже до заголовка «Форматы телефонных номеров» — там уже представлен мой вариант разбора номера с ссылкой на код.

Всеуничтожающий примитив

(Найденное решение. Мое ниже)

Первое, на что я наткнулся — были сообщения на форумах и банки скриптов, предлагающие решения следующего плана:

<?
function phone_number($sPhone){
    $sPhone = ereg_replace(«[^0-9]»,»,$sPhone);
    if(strlen($sPhone) != 10) return(False);
    $sArea = substr($sPhone, 0,3);
    $sPrefix = substr($sPhone,3,3);
    $sNumber = substr($sPhone,6,4);
    $sPhone = «(«.$sArea.«)».$sPrefix.«-«.$sNumber;
    return($sPhone);
}
?>

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

Форматирование с помощью sscanf

(Найденное решение. Мое ниже)

function formatPhone($phone) {
    if (empty($phone)) return «»;
    if (strlen($phone) == 7)
        sscanf($phone, «%3s%4s», $prefix, $exchange);
    else if (strlen($phone) == 10)
            sscanf($phone, «%3s%3s%4s», $area, $prefix, $exchange);
        else if (strlen($phone) > 10)
                if(substr($phone, 0,1)==‘1’) {
                    sscanf($phone, «%1s%3s%3s%4s», $country, $area, $prefix, $exchange);
                }
                else{
                    sscanf($phone, «%3s%3s%4s%s», $area, $prefix, $exchange, $extension);
            }
            else
                return «unknown phone format: $phone»;
    $out = «»;
    $out .= isset($country)? $country.‘ ‘: »;
    $out .= isset($area)? ‘(‘. $area. ‘) ‘: »;
    $out .= $prefix. ‘-‘. $exchange;
    $out .= isset($extension)? ‘ x’. $extension: »;
    return $out;
}

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

Symfony, lib/helpers/PhoneHelper.php, format_phone

(Найденное решение. Мое ниже)

<?php
function format_phone($phone = », $convert = false, $trim = true)
{
    // If we have not entered a phone number just return empty
    if (empty($phone)) {
        return »;
    }// Strip out any extra characters that we do not need only keep letters and numbers
    $phone = preg_replace(«/[^0-9A-Za-z]/», «», $phone);// Do we want to convert phone numbers with letters to their number equivalent?
    // Samples are: 1-800-TERMINIX, 1-800-FLOWERS, 1-800-Petmeds
    if ($convert == true) {
        $replace = array(‘2’=>array(‘a’,‘b’,‘c’),
                 ‘3’=>array(‘d’,‘e’,‘f’),
                     ‘4’=>array(‘g’,‘h’,‘i’),
                 ‘5’=>array(‘j’,‘k’,‘l’),
                                 ‘6’=>array(‘m’,‘n’,‘o’),
                 ‘7’=>array(‘p’,‘q’,‘r’,‘s’),
                 ‘8’=>array(‘t’,‘u’,‘v’), ‘9’=>array(‘w’,‘x’,‘y’,‘z’));// Replace each letter with a number
        // Notice this is case insensitive with the str_ireplace instead of str_replace 
        foreach($replace as $digit=>$letters) {
            $phone = str_ireplace($letters, $digit, $phone);
        }
    }// If we have a number longer than 11 digits cut the string down to only 11
    // This is also only ran if we want to limit only to 11 characters
    if ($trim == true && strlen($phone)>11) {
        $phone = substr($phone,  0, 11);
    } // Perform phone number formatting here
    if (strlen($phone) == 7) {
        return preg_replace(«/([0-9a-zA-Z]{3})([0-9a-zA-Z]{4})/», «$1-$2», $phone);
    } elseif (strlen($phone) == 10) {
        return preg_replace(«/([0-9a-zA-Z]{3})([0-9a-zA-Z]{3})([0-9a-zA-Z]{4})/», «($1) $2-$3», $phone);
    } elseif (strlen($phone) == 11) {
        return preg_replace(«/([0-9a-zA-Z]{1})([0-9a-zA-Z]{3})([0-9a-zA-Z]{3})([0-9a-zA-Z]{4})/», «$1($2) $3-$4», $phone);
    }// Return original phone if not 7, 10 or 11 digits long
    return $phone;
}
?>

Функция позволяет не только форматировать в XXX-XXXX, (XXX) XXX-XXXX и X (XXX) XXX-XXXX, но и конвертировать номера, написанные цифрами. Ограниченность функции в форматировании номеров длиной 7, 10 и 11 символов никак не подходит.

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

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

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

На самом деле, все оказалось не так страшно. В каждой стране можно разделить все коды городов на две части: на те, что в большинстве своем совпадают по длине, и все остальные. Этого достаточно, чтобы резко сократить область перебора кодов при сравнении. Т.е. можно создать массив из данных по каждой стране вида:

<?
$data = Array(
‘Код страны’=>Array(
        ‘name’=>‘Имя страны’, // для удобства. Не будет использоваться.
        ‘cityCodeLength’=> обычная_длина_кода_города_для_этой_страны,
        ‘exceptions’=>Array(коды_городов_исключения),
    )
);
?>

Затем провести предварительную обработку данных, дополнив его полями, сужающими область перебора, exceptions_max и exceptions_min — максимальной и минимальной длиной кода городов-исключений, соответственно. Также необходимо учесть страны, в которых коды городов начинаются на 0 — отразим эту «особенность» полем zeroHack. Как пример:

<?
$data = Array(
‘886’=>Array(
        ‘name’=>‘Taiwan’,
        ‘cityCodeLength’=>1,
        ‘zeroHack’=>false,
        ‘exceptions’=>Array(89,90,91,92,93,96,60,70,94,95),
        ‘exceptions_max’=>2,
        ‘exceptions_min’=>2
    ),
);
?>

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

<?
function phone($phone = », $convert = true, $trim = true)
{
    global $phoneCodes; // только для примера! При реализации избавиться от глобальной переменной.
    if (empty($phone)) {
        return »;
    }
    // очистка от лишнего мусора с сохранением информации о «плюсе» в начале номера
    $phone=trim($phone);
    $plus = ($phone[ 0] == ‘+’);
    $phone = preg_replace(«/[^0-9A-Za-z]/», «», $phone);
    $OriginalPhone = $phone;// конвертируем буквенный номер в цифровой
    if ($convert == true && !is_numeric($phone)) {
        $replace = array(‘2’=>array(‘a’,‘b’,‘c’),
        ‘3’=>array(‘d’,‘e’,‘f’),
        ‘4’=>array(‘g’,‘h’,‘i’),
        ‘5’=>array(‘j’,‘k’,‘l’),
        ‘6’=>array(‘m’,‘n’,‘o’),
        ‘7’=>array(‘p’,‘q’,‘r’,‘s’),
        ‘8’=>array(‘t’,‘u’,‘v’),
        ‘9’=>array(‘w’,‘x’,‘y’,‘z’));foreach($replace as $digit=>$letters) {
            $phone = str_ireplace($letters, $digit, $phone);
        }
    }// заменяем 00 в начале номера на +
    if (substr($phone,  0, 2)==«00»)
    {
        $phone = substr($phone, 2, strlen($phone)-2);
        $plus=true;
    }// если телефон длиннее 7 символов, начинаем поиск страны
    if (strlen($phone)>7)
    foreach ($phoneCodes as $countryCode=>$data)
    {
        $codeLen = strlen($countryCode);
        if (substr($phone,  0, $codeLen)==$countryCode)
        {
            // как только страна обнаружена, урезаем телефон до уровня кода города
            $phone = substr($phone, $codeLen, strlen($phone)-$codeLen);
            $zero=false;
            // проверяем на наличие нулей в коде города
            if ($data[‘zeroHack’] && $phone[ 0]==‘0’)
            {
                $zero=true;
                $phone = substr($phone, 1, strlen($phone)-1);
            }$cityCode=NULL;
            // сначала сравниваем с городами-исключениями
            if ($data[‘exceptions_max’]!= 0)
            for ($cityCodeLen=$data[‘exceptions_max’]; $cityCodeLen>=$data[‘exceptions_min’]; $cityCodeLen—)
            if (in_array(intval(substr($phone,  0, $cityCodeLen)), $data[‘exceptions’]))
            {
                $cityCode = ($zero? «0»: «»).substr($phone,  0, $cityCodeLen);
                $phone = substr($phone, $cityCodeLen, strlen($phone)-$cityCodeLen);
                break;
            }
            // в случае неудачи с исключениями вырезаем код города в соответствии с длиной по умолчанию
            if (is_null($cityCode))
            {
                $cityCode = substr($phone,  0, $data[‘cityCodeLength’]);
                $phone = substr($phone, $data[‘cityCodeLength’], strlen($phone)-$data[‘cityCodeLength’]);
            }
            // возвращаем результат
            return ($plus? «+»: «»).$countryCode.‘(‘.$cityCode.‘)’.phoneBlocks($phone);
        }
    }
    // возвращаем результат без кода страны и города
    return ($plus? «+»: «»).phoneBlocks($phone);
}// функция превращает любое число в строку формата XX-XX-… или XXX-XX-XX-… в зависимости от четности кол-ва цифр
function phoneBlocks($number){
    $add=»;
    if (strlen($number)%2)
    {
        $add = $number[ 0];
        $add .= (strlen($number)<=5? «-«: «»);
        $number = substr($number, 1, strlen($number)-1);
    }
    return $add.implode(«-«, str_split($number, 2));
}// тесты
echo phone(«+38 (044) 226-22-04»).«<br />»;
echo phone(«0038 (044) 226-22-04»).«<br />»;
echo phone(«+79263874814»).«<br />»;
echo phone(«4816145»).«<br />»;
echo phone(«+44 (0) 870 770 5370»).«<br />»;
echo phone(«0044 (0) 870 770 5370»).«<br />»;
echo phone(«+436764505509»).«<br />»;
echo phone(«(+38-048) 784-15-46 «).«<br />»;
echo phone(«(38-057) 706-34-03 «).«<br />»;
echo phone(«+38 (044) 244 12 01 «).«<br />»;
?>

, где global $phoneCodes; — тот самый массив с информацией по всем странам.

Выведет

+380(44)226-22-04<br/>+380(44)226-22-04<br/>+7(926)387-48-14<br/>481-61-45<br/>+44(0870)770-53-70<br/>+44(0870)770-53-70<br/>+43(6764)50-55-09<br/>380(4878)415-46<br/>380(5770)634-03<br/>+380(44)244-12-01

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

Самое интересное

Используя информацию с сайтов:
http://www.mtt.ru/info/def/index.wbp
http://www.hella.ru/code/codeuro.htm
http://www.scross.ru/guide/phone-global/
я собрал массив данных по всем представленным странам, включая города-исключения, флаги zeroHack, а также коды мобильных сетей. Код можно загрузить здесь.

Быстродействие

Вопреки всем самым пессимистичным ожиданиям, код отрабатывает 10.000 номеров менее чем за 2 секунды.

UPD Готовятся поправки:

  1. поддержка паттернов форматирования, принятых внутри конкретных стран («локально-принятые» нормы отображения номеров);
  2. добавление флага для указания, относительно какой страны выполнять форматирование номера;
  3. добавление параметра для указания формата вывода (в случае личных предпочтений и исключений);
  4. поддержка нелатинских буквенных номеров
  5. определение сотовых номеров и замена скобок на пробелы

UPD: Архив пропал с сервера, выложил на https://github.com/mrXCray/PhoneCodesСкоро будет обновление по поправкам выше + бонус.

Телефонные номера, которые пишут вещи, предназначены не только для корпораций с причудливыми номерами 1-800; вероятность того, что ваш номер телефона что-то заклинает. Если вам интересно, какие слова могут быть скрыты в вашем номере, проверьте номер телефона в Word. Этот веб-сервис позволяет вам конвертировать телефонный номер в слова и видеть список всех возможных буквенных комбинаций, которые позволяет этот номер.

преобразовать номер телефона в слова

Объем информации, предоставляемой этим сайтом, огромен, но если вам интересно, есть ли там какое-то определенное слово, вы всегда можете «Ctl» и «f» пройти по нему. Кроме того, вы можете прокрутить, пока слово не появится на вас.

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

  • Найти слова, спрятанные в вашем номере телефона.
  • Показывает все возможные комбинации букв в любом 7-значном номере телефона.
  • Подавляющее количество информации.
  • Аналогичный инструмент: PhoneSpell

Проверьте номер телефона для Word @ www.labrocca.com/phone

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

Перед отправкой телефона SIP-оператору, может потребоваться преобразование номера в его формат. Например, в телефоне номера часто записаны через 8, со скобками и пробелами. Такие номера для совместимости лучше привести к стандартному формату:

8 (926) 123-45-67 → 79261234567

Суть необходимого преобразования номера: удалить пробелы, скобки и дефисы. Цифру 8 в начале номера нужно преобразовать в код страны, например, для России это 7.

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

  • Имеется 2 типа правил: содержащие знак = (равенство) и не содержащие его
  • Все правила применяются по очереди
  • Рекомендуем использовать символ ` (одинарная кавычка) в начале и в конце правил, чтобы отключить возможное преобразование части правила в смайлики.

Понравилась статья? Поделить с друзьями:
  • Комсервис аскино номер телефона
  • Комплексный центр кунашакского района номер телефона
  • Комплексный центр кунашак номер телефона
  • Коммунэлектро дивное номер телефона
  • Комильфо мценск номер телефона