我可以优化这款手机 - 正则表达式吗?

时间:2008-11-06 13:56:56

标签: regex

好的,所以我有这个正则表达式:

( |^|>)(((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{2})(-)?( )?)?)([0-9]{7}))|((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{3})(-)?( )?)?)([0-9]{6}))|((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{1})(-)?( )?)?)([0-9]{8})))( |$|<)

它格式化荷兰语和比利时语电话号码(我只想要那些因此31和32作为国家代码)。

解密并不是很有趣,但正如你所看到的那样,它也有很多重复。但现在它确实非常准确地处理了它

接受以下所有欧洲格式的电话号码

0031201234567
0031223234567
0031612345678
+31(0)20-1234567
+31(0)223-234567
+31(0)6-12345678
020-1234567
0223-234567
06-12345678
0201234567
0223234567
0612345678

并且以下格式错误的

06-1234567 (mobile phone number in the Netherlands should have 8 numbers after 06 )
0223-1234567 (area code with home phone)

相反,这是好的。

020-1234567 (area code with 3 numbers has 7 numbers for the phone as opposed to a 4 number area code which can only have 6 numbers for phone number)

正如你所看到的那样' - '字符使它有点困难,但我需要它,因为它是人们通常使用的格式的一部分,我希望能够解析它们。

现在是我的问题......你是否看到了一种简化这种正则表达式的方法(如果你看到它有错误,甚至可以改进它),同时保持相同的规则?

您可以在regextester.com

进行测试

('(| ^ |&gt;)'用于检查它是否位于单词的开头,可能是以新行或'&gt;'开头。我搜索电话号码在HTML页面中。)

5 个答案:

答案 0 :(得分:12)

首先观察:阅读正则表达式是一场噩梦。它呼唤Perl的/ x模式。

第二个观察:表达中有许多,很多,并且有很多捕获括号(42如果我算得正确的话; 42当然是“对生命,宇宙和一切的答案” - 见如果你需要解释道格拉斯亚当斯“Hitchiker的银河指南”。

比尔蜥蜴注意到你多次使用'(-)?( )?'。与“-? ?”或“[- ]?”相比,没有明显的优势,除非你真的想要单独捕获实际的标点符号(但有很多捕获括号,其中'$ < em> n '要使用的项目很难)。

所以,让我们尝试编辑你的单行的副本:

( |^|>)
(
    ((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{2})(-)?( )?)?)([0-9]{7})) |
    ((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{3})(-)?( )?)?)([0-9]{6})) |
    ((((((\+|00)(31|32)( )?(\(0\))?)|0)([0-9]{1})(-)?( )?)?)([0-9]{8}))
)
( |$|<)

好的 - 现在我们可以看到正则表达式的常规结构。

从这里可以获得更多分析。是的,正则表达式可以有很大的改进。第一个显而易见的是,提取国际前缀部分,并应用一次(可选地,或要求前导零),然后应用国家规则。

( |^|>)
(
    (((\+|00)(31|32)( )?(\(0\))?)|0)
    (((([0-9]{2})(-)?( )?)?)([0-9]{7})) |
    (((([0-9]{3})(-)?( )?)?)([0-9]{6})) |
    (((([0-9]{1})(-)?( )?)?)([0-9]{8}))
)
( |$|<)

然后我们可以如前所述简化标点符号,并删除一些似乎是多余的括号,并改进国家/地区代码识别器:

( |^|>)
(
    (((\+|00)3[12] ?(\(0\))?)|0)
    (((([0-9]{2})-? ?)?)[0-9]{7}) |
    (((([0-9]{3})-? ?)?)[0-9]{6}) |
    (((([0-9]{1})-? ?)?)[0-9]{8})
)
( |$|<)

我们可以观察到正则表达式没有强制执行移动电话代码的规则(因此它并不坚持'06'后面跟着8位数字)。它似乎也允许1,2或3位'交换'代码是可选的,即使有一个国际前缀 - 可能不是你想到的,并修复它删除更多的括号。之后我们可以删除更多括号,导致:

( |^|>)
(
    (((\+|00)3[12] ?(\(0\))?)|0)    # International prefix or leading zero
    ([0-9]{2}-? ?[0-9]{7}) |        # xx-xxxxxxx
    ([0-9]{3}-? ?[0-9]{6}) |        # xxx-xxxxxx
    ([0-9]{1}-? ?[0-9]{8})          # x-xxxxxxxx
)
( |$|<)

你可以从这里进一步优化,我希望。

答案 1 :(得分:8)

善良的万能之王,多么糟糕! :)如果您有高级语义或业务规则(例如您描述的那些谈论欧洲数字,荷兰的数字等),您可能会更好地将单个正则表达式测试分解为几个单独的正则表达式测试,每个高级规则都有一个。

if number =~ /...../  # Dutch mobiles
  # ...
elsif number =~ /..../  # Belgian landlines
  # ...
# etc.
end

阅读和维护以及改变这种方式会更容易。

答案 2 :(得分:3)

将其拆分为多个表达式。例如(伪代码)......

phone_no_patterns = [
    /[0-9]{13}/, # 0031201234567
    /+(31|32)\(0\)\d{2}-\d{7}/ # +31(0)20-1234567
    # ..etc..
]
def check_number(num):
    for pattern in phone_no_patterns:
        if num matches pattern:
            return match.groups

然后你只需遍历每个模式,检查每个模式是否匹配..

将模式拆分起来可以很容易地修复导致问题的特定数字(这对于单个整体正则表达式来说会很糟糕)

答案 3 :(得分:3)

(31 | 32)看起来很糟糕。匹配32时,正则表达式引擎将首先尝试匹配31(2个字符),失败,并回溯两个字符以匹配31.首先匹配3(一个字符),尝试1(失败),回溯一个字符和更高效比赛2。

当然,你的正则表达式在0800-数字上失败了;它们不是10位数。

答案 4 :(得分:2)

这不是优化,而是使用

(-)?( )?
你的正则表达式中有三次。这将使您匹配这些电话号码

+31(0)6-12345678
+31(0)6 12345678

但也会匹配包含短划线后跟空格的数字,例如

+31(0)6- 12345678

您可以替换

(-)?( )?

(-| )?

匹配短划线空格。