Подтверждение номера телефона в США с помощью php / regex - PullRequest
11 голосов
/ 29 июля 2010

РЕДАКТИРОВАТЬ: Я смешал и изменил два ответа, приведенных ниже, чтобы сформировать полную функцию, которая теперь выполняет то, что я хотел, а затем некоторые ... Так что я решил, что я опубликуйте это здесь на случай, если кто-то еще придет искать эту же вещь.

/*
 * Function to analyze string against many popular formatting styles of phone numbers
 * Also breaks phone number into it's respective components
 * 3-digit area code, 3-digit exchange code, 4-digit subscriber number
 * After which it validates the 10 digit US number against NANPA guidelines
*/
function validPhone($phone) {

  $format_pattern = '/^(?:(?:\((?=\d{3}\)))?(\d{3})(?:(?<=\(\d{3})\))?[\s.\/-]?)?(\d{3})[\s\.\/-]?(\d{4})\s?(?:(?:(?:(?:e|x|ex|ext)\.?\:?|extension\:?)\s?)(?=\d+)(\d+))?$/';
  $nanpa_pattern = '/^(?:1)?(?(?!(37|96))[2-9][0-8][0-9](?<!(11)))?[2-9][0-9]{2}(?<!(11))[0-9]{4}(?<!(555(01([0-9][0-9])|1212)))$/';

  //Set array of variables to false initially
  $valid = array(
    'format' => false,
    'nanpa' => false,
    'ext' => false,
    'all' => false
  );

  //Check data against the format analyzer
  if(preg_match($format_pattern, $phone, $matchset)) {
    $valid['format'] = true;    
  }

  //If formatted properly, continue
  if($valid['format']) {

    //Set array of new components
    $components = array(
      'ac' => $matchset[1], //area code
      'xc' => $matchset[2], //exchange code
      'sn' => $matchset[3], //subscriber number
      'xn' => $matchset[4], //extension number
    );

    //Set array of number variants
    $numbers = array(
      'original' => $matchset[0],
      'stripped' => substr(preg_replace('[\D]', '', $matchset[0]), 0, 10)
    );

    //Now let's check the first ten digits against NANPA standards
    if(preg_match($nanpa_pattern, $numbers['stripped'])) {
      $valid['nanpa'] = true;
    }

    //If the NANPA guidelines have been met, continue
    if($valid['nanpa']) {
      if(!empty($components['xn'])) {
        if(preg_match('/^[\d]{1,6}$/', $components['xn'])) {
          $valid['ext'] = true;
        }
      }
      else {
        $valid['ext'] = true;
      }
    }

    //If the extension number is valid or non-existent, continue
    if($valid['ext']) {
      $valid['all'] = true;
    }
  }
  return $valid['all'];
}

Ответы [ 5 ]

15 голосов
/ 29 июля 2010

Вы можете решить эту проблему, используя прогнозное утверждение .По сути, мы говорим, что я хочу серию конкретных букв (e, ex, ext, x, extension), за которыми следует одно или несколько чисел.Но мы также хотим охватить случай, когда расширение вообще отсутствует.

Примечание: вам не нужны скобки вокруг одиночных символов, таких как [\ s] или [x], которые следуют.Кроме того, вы можете группировать символы, которые должны находиться в одном месте, поэтому вместо \ s? \.? /? Можно использовать [\ s \ ./]?что означает «один из этих символов»

Вот обновление с регулярным выражением, которое также разрешает ваш комментарий.Я добавил объяснение в фактический код.

<?php
    $sPattern = "/^
        (?:                                 # Area Code
            (?:                            
                \(                          # Open Parentheses
                (?=\d{3}\))                 # Lookahead.  Only if we have 3 digits and a closing parentheses
            )?
            (\d{3})                         # 3 Digit area code
            (?:
                (?<=\(\d{3})                # Closing Parentheses.  Lookbehind.
                \)                          # Only if we have an open parentheses and 3 digits
            )?
            [\s.\/-]?                       # Optional Space Delimeter
        )?
        (\d{3})                             # 3 Digits
        [\s\.\/-]?                          # Optional Space Delimeter
        (\d{4})\s?                          # 4 Digits and an Optional following Space
        (?:                                 # Extension
            (?:                             # Lets look for some variation of 'extension'
                (?:
                    (?:e|x|ex|ext)\.?       # First, abbreviations, with an optional following period
                |
                    extension               # Now just the whole word
                )
                \s?                         # Optionsal Following Space
            )
            (?=\d+)                         # This is the Lookahead.  Only accept that previous section IF it's followed by some digits.
            (\d+)                           # Now grab the actual digits (the lookahead doesn't grab them)
        )?                                  # The Extension is Optional
        $/x";                               // /x modifier allows the expanded and commented regex

    $aNumbers = array(
        '123-456-7890x123',
        '123.456.7890x123',
        '123 456 7890 x123',
        '(123) 456-7890 x123',
        '123.456.7890x.123',
        '123.456.7890 ext. 123',
        '123.456.7890 extension 123456',
        '123 456 7890', 
        '123-456-7890ex123',
        '123.456.7890 ex123',
        '123 456 7890 ext123',
        '456-7890',
        '456 7890',
        '456 7890 x123',
        '1234567890',
        '() 456 7890'
    );

    foreach($aNumbers as $sNumber) {
        if (preg_match($sPattern, $sNumber, $aMatches)) {
            echo 'Matched ' . $sNumber . "\n";
            print_r($aMatches);
        } else {
            echo 'Failed ' . $sNumber . "\n";
        }
    }
?>

И вывод:

Matched 123-456-7890x123
Array
(
    [0] => 123-456-7890x123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 123.456.7890x123
Array
(
    [0] => 123.456.7890x123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 123 456 7890 x123
Array
(
    [0] => 123 456 7890 x123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched (123) 456-7890 x123
Array
(
    [0] => (123) 456-7890 x123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 123.456.7890x.123
Array
(
    [0] => 123.456.7890x.123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 123.456.7890 ext. 123
Array
(
    [0] => 123.456.7890 ext. 123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 123.456.7890 extension 123456
Array
(
    [0] => 123.456.7890 extension 123456
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123456
)
Matched 123 456 7890
Array
(
    [0] => 123 456 7890
    [1] => 123
    [2] => 456
    [3] => 7890
)
Matched 123-456-7890ex123
Array
(
    [0] => 123-456-7890ex123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 123.456.7890 ex123
Array
(
    [0] => 123.456.7890 ex123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 123 456 7890 ext123
Array
(
    [0] => 123 456 7890 ext123
    [1] => 123
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 456-7890
Array
(
    [0] => 456-7890
    [1] => 
    [2] => 456
    [3] => 7890
)
Matched 456 7890
Array
(
    [0] => 456 7890
    [1] => 
    [2] => 456
    [3] => 7890
)
Matched 456 7890 x123
Array
(
    [0] => 456 7890 x123
    [1] => 
    [2] => 456
    [3] => 7890
    [4] => 123
)
Matched 1234567890
Array
(
    [0] => 1234567890
    [1] => 123
    [2] => 456
    [3] => 7890
)
Failed () 456 7890
4 голосов
/ 29 июля 2010

Текущий REGEX

/^[\(]?(\d{0,3})[\)]?[\.]?[\/]?[\s]?[\-]?(\d{3})[\s]?[\.]?[\/]?[\-]?(\d{4})[\s]?[x]?(\d*)$/

имеет много проблем, в результате чего он соответствует всем следующим, среди прочего:
(0./ -000 ./-0000 x00000000000000000000000)
()./1234567890123456789012345678901234567890
\)\-555/1212 x

Я думаю, этот REGEX ближе к тому, что вы ищете:

/^(?:(?:(?:1[.\/\s-]?)(?!\())?(?:\((?=\d{3}\)))?((?(?!(37|96))[2-9][0-8][0-9](?<!(11)))?[2-9])(?:\((?<=\(\d{3}))?)?[.\/\s-]?([0-9]{2}(?<!(11)))[.\/\s-]?([0-9]{4}(?<!(555(01([0-9][0-9])|1212))))(?:[\s]*(?:(?:x|ext|extn|ex)[.:]*|extension[:]?)?[\s]*(\d+))?$/

или в разобранном виде:

<?
    $pattern = 
    '/^                                                     #  Matches from beginning of string

        (?:                                                 #  Country / Area Code Wrapper [not captured]
            (?:                                             #  Country Code Wrapper [not captured]
                (?:                                         #  Country Code Inner Wrapper [not captured]
                    1                                       #  1 - CC for United States and Canada
                    [.\/\s-]?                               #  Character Class ('.', '/', '-' or whitespace) for allowed (optional, single) delimiter between Country Code and Area Code
                )                                           #  End of Country Code
                (?!\()                                      #  Lookahead, only allowed if not followed by an open parenthesis
            )?                                              #  Country Code Optional
            (?:                                             #  Opening Parenthesis Wrapper [not captured]
                \(                                          #  Opening parenthesis
                (?=\d{3}\))                                 #  Lookahead, only allowed if followed by 3 digits and closing parenthesis [lookahead never captured]
            )?                                              #  Parentheses Optional
            ((?(?!(37|96))[2-9][0-8][0-9](?<!(11)))?[2-9])  #  3-digit NANPA-valid Area Code [captured]
            (?:                                             #  Closing Parenthesis Wrapper [not captured]
                \(                                          #  Closing parenthesis
                (?<=\(\d{3})                                #  Lookbehind, only allowed if preceded by 3 digits and opening parenthesis [lookbehind never captured]
            )?                                              #  Parentheses Optional
        )?                                                  #  Country / Area Code Optional

        [.\/\s-]?                                           #  Character Class ('.', '/', '-' or whitespace) for allowed (optional, single) delimiter between Area Code and Central-office Code

        ([0-9]{2}(?<!(11)))                                 #  3-digit NANPA-valid Central-office Code [captured]

        [.\/\s-]?                                           #  Character Class ('.', '/', '-' or whitespace) for allowed (optional, single) delimiter between Central-office Code and Subscriber number

        ([0-9]{4}(?<!(555(01([0-9][0-9])|1212))))           #  4-digit NANPA-valid Subscriber Number [captured]

        (?:                                                 #  Extension Wrapper [not captured]
            [\s]*                                           #  Character Class for allowed delimiters (optional, multiple) between phone number and extension
            (?:                                             #  Wrapper for extension description text [not captured]
                (?:x|ext|extn|ex)[.:]*                      #  Abbreviated extensions with character class for terminator (optional, multiple) [not captured]
              |                                             #  OR
                extension[:]?                               #  The entire word extension with character class for optional terminator
            )?                                              #  Marker for Extension optional
            [\s]*                                           #  Character Class for allowed delimiters (optional, multiple) between extension description text and actual extension
            (\d+)                                           #  Extension [captured if present], required for extension wrapper to match
        )?                                                  #  Entire extension optional

    $                                                       #  Matches to end of string
    /x';                                                    // /x modifier allows the expanded and commented regex

?>

Эта модификация предоставляет несколько улучшений.

  1. Создает настраиваемую группу элементов, которые могут соответствовать как расширение.Вы можете добавить дополнительные разделители для расширения. Это был исходный запрос. Расширение также допускает двоеточие после разделителя расширения.
  2. Преобразует последовательность из 4 необязательных разделителей (точка, пробел, косая черта или дефис) в символкласс, который соответствует только одному.
  3. Он группирует элементы соответствующим образом.В данном примере у вас могут быть открывающие круглые скобки без кода зоны между ними, и вы можете иметь метку расширения (пробел-x) без расширения.Это альтернативное регулярное выражение требует либо полного кода города, либо его отсутствия, либо полного внутреннего номера или его отсутствия.
  4. 4 компонента номера (код города, код центрального офиса, номер телефона и добавочный номер) имеют обратную ссылкуэлементы, которые вводятся в $ match в preg_match().
  5. . Использует lookahead / lookbehind, чтобы требовать совпадения скобок в коде области.
  6. Позволяет использовать 1- перед числом.(Предполагается, что все числа являются номерами США или Канады, что представляется разумным, поскольку в конечном итоге проводится сопоставление с ограничениями NANPA. Также запрещается сочетание префикса кода страны и кода зоны, заключенных в скобки.
  7. Он объединяется в NANPAправила устранения недопустимых телефонных номеров.
    1. Исключает коды городов в форме 0xx, 1xx 37x, 96x, x9x и x11, которые являются недействительными кодами NANPA.
    2. Устраняет коды центрального офисав форме 0xx и 1xx (недействительные коды центрального офиса NANPA).
    3. Исключает номера с формой 555-01xx (не присваивается из NANPA).

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

  1. Нет ничего, что требовало бы повторного использования одного и того же разделителя, учитывая числа, например 800-555.1212, 800/555 1212, 800 555.1212 и т. Д.
  2. Нет ничего, что могло бы ограничить разделитель после области трескиe с круглыми скобками, с учетом чисел, таких как (800) -555-1212 или (800) /5551212.

Правила NANPA адаптированы из следующего REGEX, найденного здесь: http://blogchuck.com/2010/01/php-regex-for-validating-phone-numbers/

/^(?:1)?(?(?!(37|96))[2-9][0-8][0-9](?<!(11)))?[2-9][0-9]{2}(?<!(11))[0-9]{4}(?<!(555(01([0-9][0-9])|1212)))$/
3 голосов
/ 29 июля 2010

В качестве альтернативы, вы можете использовать довольно простой и понятный JavaScript, чтобы заставить пользователя вводить в гораздо более заданном формате.Плагин маскированного ввода (http://digitalbush.com/projects/masked-input-plugin/) для jQuery позволяет маскировать ввод HTML в качестве телефонного номера, позволяя пользователю вводить номер только в формате xxx-xxx-xxxx.Это не решает проблемы расширения, но обеспечивает гораздо более понятный пользовательский интерфейс.

3 голосов
/ 29 июля 2010

Почему бы не преобразовать любую серию букв в "х".Тогда таким образом у вас будут все возможности, преобразованные в «x».

ИЛИ

Проверьте на 3digits, 3digits, 4digits, 1orMoreDigits и игнорируйте любые другие символы между

Regex: ([0-9]{3}).*?([0-9]{3}).*?([0-9]{4}).+?([0-9]{1,})

0 голосов
/ 29 июля 2010

Ну, вы могли бы изменить регулярное выражение, но это будет не очень хорошо - вы должны разрешить "extn"?Как насчет "экстен"?Как насчет «и тогда вам нужно набрать»?

Я думаю, что «правильный» способ сделать это - добавить отдельное числовое поле формы расширения.

Но если вы действительно хотитерегулярное выражение, я думаю, я исправил это.Подсказка: вам не нужно [x] для одного символа, подойдет x.

/^\(?(\d{0,3})\)?(\.|\/)|\s|\-)?(\d{3})(\.|\/)|\s|\-)?(\d{4})\s?(x|ext)?(\d*)$/

Вы допустили точку, косую черту, тире, и пробелперсонаж.Вы должны разрешить только один из этих вариантов.Вам нужно обновить ссылки на $matches;полезные группы теперь 0, 2 и 4.

PS Это не проверено, так как у меня нет эталонного внедрения PHP.Извините за ошибки, пожалуйста, дайте мне знать, если вы найдете какие-либо, и я постараюсь их исправить.

Редактировать

Это суммируется гораздо лучше, чем я могу здесь .

...