编辑: 我已经混合并修改了下面给出的两个答案,以形成完整的功能,现在可以做我想要的,然后一些...所以我想我d发布在这里以防其他人来寻找同样的事情。
/*
* 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'];
}
答案 0 :(得分:15)
您可以使用lookahead assertion解决此问题。基本上我们所说的是我想要一系列特定字母,(e,ex,ext,x,扩展名)后跟一个或多个数字。但我们也希望涵盖根本没有扩展的情况。
侧面注意,您不需要括号 围绕单个字符,如[\ 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
答案 1 :(得分:4)
目前的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
?>
此修改提供了一些改进。
preg_match()
中的$匹配。它有一些小的限制。它们可能并不重要,但在这里被注意到了。
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)))$/
答案 2 :(得分:3)
为什么不将任何系列的字母转换为“x”。那么你就可以将所有可能性转换为“x”。
OR
检查3digits,3digits,4digits,1orMoreDigits并忽略其中任何其他字符
正则表达式:
([0-9]{3}).*?([0-9]{3}).*?([0-9]{4}).+?([0-9]{1,})
答案 3 :(得分:3)
或者,您可以使用一些非常简单明了的JavaScript来强制用户以更加指定的格式输入。 jQuery的Masked Input Plugin(http://digitalbush.com/projects/masked-input-plugin/)允许您将HTML输入屏蔽为电话号码,仅允许此人输入格式为xxx-xxx-xxxx的数字。它不能解决您的扩展程序问题,但确实可以提供更清晰的用户体验。
答案 4 :(得分:0)
好吧,你可以修改正则表达式,但它不会很好 - 你应该允许“extn”吗? “范围”怎么样?怎么样“然后你必须拨打”?
我认为“正确”的方法是添加一个单独的数字扩展表格框。
但如果你真的想要正则表达式,我想我已经修好了。提示:对于单个字符,您不需要[x]
,x
会这样做。
/^\(?(\d{0,3})\)?(\.|\/)|\s|\-)?(\d{3})(\.|\/)|\s|\-)?(\d{4})\s?(x|ext)?(\d*)$/
你允许使用点,斜线,短划线,和空格字符。您应该只允许其中一个选项。您需要更新$matches
的引用;有用的组现在是0,2和4。
P.S。这是未经测试的,因为我没有PHP运行的参考实现。对于错误表示道歉,如果您发现任何错误,请告诉我。我会尽力解决。
这总结得比我here好得多。