Проверка американского номера телефона с php/regex

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

/*
 * 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'];
}
11
задан Josh 15 November 2011 в 04:43
поделиться

5 ответов

Вы можете решить эту проблему с помощью утверждения опережающего просмотра . По сути, мы говорим, что мне нужна серия конкретных букв (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
15
ответ дан 3 December 2019 в 04:12
поделиться

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

3
ответ дан 3 December 2019 в 04:12
поделиться

Текущий 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. Четыре компонента номера (код города, код центрального офиса, номер телефона и добавочный номер) являются элементами с обратной ссылкой, которые передаются в $matches в 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. Нет никаких ограничений на использование разделителя после кода города с круглыми скобками, что позволяет использовать такие номера, как (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)))$/
4
ответ дан 3 December 2019 в 04:12
поделиться

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

ИЛИ

Проверить наличие 3-х, 3-х, 4-х, 1 или более цифр и игнорировать любые другие символы между

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

3
ответ дан 3 December 2019 в 04:12
поделиться

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

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

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

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

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

P.S. Это не проверено, поскольку у меня нет эталонной реализации работающего PHP. Приносим извинения за ошибки, дайте мне знать, если вы обнаружите их, и я постараюсь их исправить.

Править

Это обобщено намного лучше, чем я могу здесь .

0
ответ дан 3 December 2019 в 04:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: