我试图把一个全面的正则表达式放在一起来验证电话号码。理想情况下,它可以处理国际格式,但它必须处理美国格式,包括以下内容:

1-234-567-8901 1-234-567-8901 x1234 1-234-567-8901 ext1234 1 (234) 567-8901 1.234.567.8901 1/234/567/8901 12345678901

我会用我目前的尝试来回答,但我希望有人有更好的和/或更优雅的东西。


当前回答

我在另一个SO问题上回答了这个问题,然后决定把我的答案也包括在这个线程上,因为没有人解决如何要求/不要求项目,只是分发正则表达式: 正则表达式工作错误,匹配意想不到的东西

从我在那个网站上的帖子中,我创建了一个快速指南,帮助任何人为自己想要的电话号码格式制作自己的正则表达式,我要警告(就像我在另一个网站上所做的那样),如果你限制太多,你可能不会得到想要的结果,并且没有“一宜之计”的解决方案来接受世界上所有可能的电话号码——只有你决定接受的格式作为你选择的格式。使用风险自负。

快速小抄

Start the expression: /^ If you want to require a space, use: [\s] or \s If you want to require parenthesis, use: [(] and [)] . Using \( and \) is ugly and can make things confusing. If you want anything to be optional, put a ? after it If you want a hyphen, just type - or [-] . If you do not put it first or last in a series of other characters, though, you may need to escape it: \- If you want to accept different choices in a slot, put brackets around the options: [-.\s] will require a hyphen, period, or space. A question mark after the last bracket will make all of those optional for that slot. \d{3} : Requires a 3-digit number: 000-999. Shorthand for [0-9][0-9][0-9]. [2-9] : Requires a digit 2-9 for that slot. (\+|1\s)? : Accept a "plus" or a 1 and a space (pipe character, |, is "or"), and make it optional. The "plus" sign must be escaped. If you want specific numbers to match a slot, enter them: [246] will require a 2, 4, or 6. (?:77|78) or [77|78] will require 77 or 78. $/ : End the expression

其他回答

我在另一个SO问题上回答了这个问题,然后决定把我的答案也包括在这个线程上,因为没有人解决如何要求/不要求项目,只是分发正则表达式: 正则表达式工作错误,匹配意想不到的东西

从我在那个网站上的帖子中,我创建了一个快速指南,帮助任何人为自己想要的电话号码格式制作自己的正则表达式,我要警告(就像我在另一个网站上所做的那样),如果你限制太多,你可能不会得到想要的结果,并且没有“一宜之计”的解决方案来接受世界上所有可能的电话号码——只有你决定接受的格式作为你选择的格式。使用风险自负。

快速小抄

Start the expression: /^ If you want to require a space, use: [\s] or \s If you want to require parenthesis, use: [(] and [)] . Using \( and \) is ugly and can make things confusing. If you want anything to be optional, put a ? after it If you want a hyphen, just type - or [-] . If you do not put it first or last in a series of other characters, though, you may need to escape it: \- If you want to accept different choices in a slot, put brackets around the options: [-.\s] will require a hyphen, period, or space. A question mark after the last bracket will make all of those optional for that slot. \d{3} : Requires a 3-digit number: 000-999. Shorthand for [0-9][0-9][0-9]. [2-9] : Requires a digit 2-9 for that slot. (\+|1\s)? : Accept a "plus" or a 1 and a space (pipe character, |, is "or"), and make it optional. The "plus" sign must be escaped. If you want specific numbers to match a slot, enter them: [246] will require a 2, 4, or 6. (?:77|78) or [77|78] will require 77 or 78. $/ : End the expression

这是我目前为止最好的尝试。它处理上面的格式,但我确信我错过了一些其他可能的格式。

^\d?(?:(?:[\+]?(?:[\d]{1,3}(?:[ ]+|[\-.])))?[(]?(?:[\d]{3})[\-/)]?(?:[ ]+)?)?(?:[a-zA-Z2-9][a-zA-Z0-9 \-.]{6,})(?:(?:[ ]+|[xX]|(i:ext[\.]?)){1,2}(?:[\d]{1,5}))?$

Java为有效的电话号码生成REGEX

另一种替代方法是让Java生成一个REGEX,该REGEX处理从列表中读取的所有电话号码变体。这意味着名为validPhoneNumbersFormat的列表(如下面的代码上下文所示)决定哪种电话号码格式是有效的。

注意:这种类型的算法适用于任何处理正则表达式的语言。

生成REGEX的代码片段:

Set<String> regexSet = uniqueValidPhoneNumbersFormats.stream()
        .map(s -> s.replaceAll("\\+", "\\\\+"))
        .map(s -> s.replaceAll("\\d", "\\\\d"))
        .map(s -> s.replaceAll("\\.", "\\\\."))
        .map(s -> s.replaceAll("([\\(\\)])", "\\\\$1"))
        .collect(Collectors.toSet());

String regex = String.join("|", regexSet);

上下文中的代码片段:

public class TestBench {

    public static void main(String[] args) {
        List<String> validPhoneNumbersFormat = Arrays.asList(
                "1-234-567-8901",
                "1-234-567-8901 x1234",
                "1-234-567-8901 ext1234",
                "1 (234) 567-8901",
                "1.234.567.8901",
                "1/234/567/8901",
                "12345678901",
                "+12345678901",
                "(234) 567-8901 ext. 123",
                "+1 234-567-8901 ext. 123",
                "1 (234) 567-8901 ext. 123",
                "00 1 234-567-8901 ext. 123",
                "+210-998-234-01234",
                "210-998-234-01234",
                "+21099823401234",
                "+210-(998)-(234)-(01234)",
                "(+351) 282 43 50 50",
                "90191919908",
                "555-8909",
                "001 6867684",
                "001 6867684x1",
                "1 (234) 567-8901",
                "1-234-567-8901 x1234",
                "1-234-567-8901 ext1234",
                "1-234 567.89/01 ext.1234",
                "1(234)5678901x1234",
                "(123)8575973",
                "(0055)(123)8575973"
        );

        Set<String> uniqueValidPhoneNumbersFormats = new LinkedHashSet<>(validPhoneNumbersFormat);

        List<String> invalidPhoneNumbers = Arrays.asList(
                "+210-99A-234-01234",       // FAIL
                "+210-999-234-0\"\"234",    // FAIL
                "+210-999-234-02;4",        // FAIL
                "-210+998-234-01234",       // FAIL
                "+210-998)-(234-(01234"     // FAIL
        );
        List<String> invalidAndValidPhoneNumbers = new ArrayList<>();
        invalidAndValidPhoneNumbers.addAll(invalidPhoneNumbers);
        invalidAndValidPhoneNumbers.addAll(uniqueValidPhoneNumbersFormats);

        Set<String> regexSet = uniqueValidPhoneNumbersFormats.stream()
                .map(s -> s.replaceAll("\\+", "\\\\+"))
                .map(s -> s.replaceAll("\\d", "\\\\d"))
                .map(s -> s.replaceAll("\\.", "\\\\."))
                .map(s -> s.replaceAll("([\\(\\)])", "\\\\$1"))
                .collect(Collectors.toSet());

        String regex = String.join("|", regexSet);

        List<String> result = new ArrayList<>();
        Pattern pattern = Pattern.compile(regex);
        for (String phoneNumber : invalidAndValidPhoneNumbers) {
            Matcher matcher = pattern.matcher(phoneNumber);
            if(matcher.matches()) {
                result.add(matcher.group());
            }
        }

        // Output:
        if(uniqueValidPhoneNumbersFormats.size() == result.size()) {
            System.out.println("All valid numbers was matched!\n");
        }    
        result.forEach(System.out::println); 
    }

}

输出:

All valid numbers was matched!

1-234-567-8901
1-234-567-8901 x1234
1-234-567-8901 ext1234
...
...
...
/\b(\d{3}[^\d]{0,2}\d{3}[^\d]{0,2}\d{4})\b/

如果讨论的是表单验证,那么验证正确含义和正确数据的regexp将非常复杂,因为不同国家和提供者的标准各不相同。它也很难跟上时代。

我把这个问题解释为寻找一个广泛有效的模式,这个模式可能在内部不一致——例如,拥有一组有效的数字,但不验证中干线、交换机等与国家代码前缀的有效模式是否一致。

北美是简单明了的,而在国际上,我更喜欢使用一种“惯用”模式,即人们指定和记忆数字的方式:

^((((\(\d{3}\))|(\d{3}-))\d{3}-\d{4})|(\+?\d{2}((-| )\d{1,8}){1,5}))(( x| ext)\d{1,5}){0,1}$

北美模式确保如果包含一个括号,则两个括号都包含。国际帐户包含可选的首字母“+”和国家代码。在那之后,你就进入了成语。有效的匹配将是:

(xxx) xxx-xxxx (xxx) -xxx-xxxx (xxx) xxx-xxxx x123 12 1234 123 1 x1111 12 12 12 12 12 1 1234 123456 x12345 +12 1234 1234 +12 12 1234 +12 1234 5678 + 12 12345678

这可能有偏见,因为我的经验仅限于北美、欧洲和一小部分亚洲。