Convalida il numero di telefono degli Stati Uniti con php / regex

EDIT: Ho mescolato e modificato due delle risposte fornite di seguito per formare la function completa che ora fa ciò che volevo e poi alcuni … Quindi ho pensato di postarlo qui nel caso in cui qualcun altro venga a cercare lo stesso cosa.

/* * 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 arrays of variables to false initially $valid = arrays( '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 arrays of new components $components = arrays( 'ac' => $matchset[1], //area code 'xc' => $matchset[2], //exchange code 'sn' => $matchset[3], //subscriber number 'xn' => $matchset[4], //extension number ); //Set arrays of number variants $numbers = arrays( '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']; } 

Puoi risolvere ciò usando un'asserzione lookahead . Fondamentalmente quello che stiamo dicendo è che voglio una serie di lettere specifiche, (e, ex, ext, x, estensione) seguite da uno o più numbers. Ma vogliamo anche coprire il caso in cui non vi è alcuna estensione.

Nota a margine, non hai bisogno di parentesi attorno a singoli caratteri come [\ s] o quella [x] che segue. Inoltre, puoi raggruppare i caratteri che devono essere nello stesso punto, quindi invece di \ s? \.? / ?, puoi usare [\ s \ ./]? che significa "uno di questi personaggi"

Ecco un aggiornamento con espressioni regolari che risolve il tuo commento anche qui. Ho aggiunto la spiegazione nel codice reale.

 <?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 = arrays( '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"; } } ?> 

E l'output:

 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 

L'attuale REGEX

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

ha un sacco di problemi, risultando in esso corrispondente a tutti i seguenti, tra gli altri:
(0./ -000 ./-0000 x00000000000000000000000)
()./1234567890123456789012345678901234567890
\)\-555/1212 x

Penso che questo REGEX sia più vicino a quello che stai cercando:

 /^(?:(?:(?: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+))?$/ 

o, esploso:

 <? $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 ?> 

Questa modifica fornisce numerosi miglioramenti.

  1. Crea un gruppo configurabile di elementi che possono corrispondere come l'estensione. È ansible aggiungere ulteriori delimitatori per l'estensione. Questa era la richiesta originale. L'estensione consente anche i due punti dopo il delimitatore di estensione.
  2. Converte la sequenza di 4 delimitatori opzionali (punto, spazio bianco, barra o trattino) in una class di caratteri che corrisponde solo a una singola.
  3. Raggruppa le voci in modo appropriato. Nell'esempio fornito, è ansible avere tra parentesi le parentesi di apertura senza un prefisso, e si può avere il segno di estensione (spazio-x) senza un'estensione. Questa espressione regolare alternativa richiede un prefisso completo o nessuno e un'estensione completa o nessuna.
  4. I 4 componenti del numero (prefisso, codice dell'ufficio centrale, numero di telefono ed estensione) sono gli elementi di riferimento che si alimentano in partite $ in preg_match() .
  5. Utilizza lookahead / lookbehind per richiedere parentesi corrispondenti nel prefisso.
  6. Consente di utilizzare un 1- prima del numero. (Ciò presuppone che tutti i numbers siano numbers statunitensi o canadesi, il che sembra ragionevole dal momento che la partita è in definitiva effettuata a fronte di restrizioni NANPA. Inoltre, non consente la combinazione del prefisso del codice paese e del prefisso del paese racchiusi tra parentesi.
  7. Si fonde con le regole NANPA per eliminare i numbers di telefono non assegnabili.
    1. Elimina i prefissi nel formato 0xx, 1xx 37x, 96x, x9x e x11 che sono codici area NANPA non validi.
    2. Elimina i codici dell'ufficio centrale nel formato 0xx e 1xx (codici NANPA centrali non validi).
    3. Elimina i numbers con il module 555-01xx (non assegnabile da NANPA).

Ha alcune limitazioni minori. Probabilmente non sono importnti, ma vengono notati qui.

  1. Non c'è nulla che richieda che lo stesso delimitatore venga usato ripetutamente, consentendo numbers come 800-555.1212, 800/555 1212, 800 555.1212 ecc.
  2. Non c'è nulla in atto per limitare il delimitatore dopo un codice area con parentesi, consentendo numbers come (800) -555-1212 o (800) / 5551212.

Le regole NANPA sono adattate dal seguente REGEX, trovato qui: 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)))$/ 

Perché non convertire alcuna serie di lettere in "x". In questo modo avresti convertito tutte le possibilità in "x".

O

Verifica di 3digits, 3digits, 4digits, 1orMoreDigits e ignora qualsiasi altro carattere tra

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

In alternativa, potresti usare un semplice e semplice JavaScript per forzare l'utente a entrare in un formato molto più specifico. Il plug-in di input mascherato ( http://digitalbush.com/projects/masked-input-plugin/ ) per jQuery consente di mascherare un input HTML come numero di telefono, consentendo solo alla persona di immettere un numero nel formato xxx-xxx -xxxx. Non risolve i problemi di estensione, ma fornisce un'esperienza utente molto più pulita.

Beh, potresti modificare la regex, ma non sarà molto bello – dovresti consentire "extn"? Che ne dici di "extentn"? Che ne dici di "e quindi devi comporre"?

Penso che il modo "giusto" per farlo sia quello di aggiungere una casella di forma separata, numbersca, di estensione.

Ma se vuoi davvero la regex, penso di aver risolto il problema. Suggerimento: non hai bisogno di [x] per un singolo carattere, x lo farà.

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

Hai permesso un punto, una barra, un trattino e un carattere di spazio bianco. Dovresti consentire solo una di queste opzioni. Dovrai aggiornare i riferimenti alle $matches ; i gruppi utili ora sono 0, 2 e 4.

PS Questo non è verificato, dal momento che non ho un implanto di riferimento su PHP in esecuzione. Ci scusiamo per gli errori, per favore fathemes sapere se ne trovate e cercherò di risolverli.

modificare

Questo è riassunto molto meglio di quello che posso qui .