2008-12-22 88 views
2

我正在处理一段代码,将手机号码变为手机链接 - 我已经知道了,但感觉真的很脏。电话号码到Python中的链接

import re 
from string import digits 

PHONE_RE = re.compile('([(]{0,1}[2-9]\d{2}[)]{0,1}[-_. ]{0,1}[2-9]\d{2}[-_. ]{0,1}\d{4})') 

def numbers2links(s): 
    result = "" 
    last_match_index = 0 
    for match in PHONE_RE.finditer(s): 
      raw_number = match.group() 
      number = ''.join(d for d in raw_number if d in digits) 
      call = '<a href="tel:%s">%s</a>' % (number, raw_number) 
      result += s[last_match_index:match.start()] + call 
      last_match_index = match.end() 
    result += s[last_match_index:] 
    return result 

>>> numbers2links("Ghost Busters at (555) 423-2368! How about this one: 555 456 7890! 555-456-7893 is where its at.") 
'Ghost Busters at <a href="tel:5554232368">(555) 423-2368</a>! How about this one: <a href="tel:5554567890">555 456 7890</a>! <a href="tel:5554567893">555-456-7893</a> is where its at.' 

反正我有可能重组正则表达式或我使用,使这种清洁的正则表达式的方法?

更新

为了澄清,我的问题是不是我的正则表达式的正确性 - 我意识到,这是有限的。相反,我想知道是否有人对替代电话号码链接的方法有任何意见 - 无论如何,我可以使用re.replace或类似的东西,而不是我有的字符串hackery?

回答

5

不错第一次:)我认为这个版本更具可读性(可能更快一点)。这里要注意的关键是使用re.sub。让我们从讨厌匹配索引了......

import re 

PHONE_RE = re.compile('([(]{0,1}[2-9]\d{2}[)]{0,1}[-_. ]{0,1}[2-9]\d{2}[-_. ]{0,1}\d{4})') 
NON_NUMERIC = re.compile('\D') 

def numbers2links(s): 

    def makelink(mo): 
     raw_number = mo.group() 
     number = NON_NUMERIC.sub("", raw_number) 
     return '<a href="tel:%s">%s</a>' % (number, raw_number) 

    return PHONE_RE.sub(makelink, s) 


print numbers2links("Ghost Busters at (555) 423-2368! How about this one: 555 456 7890! 555-456-7893 is where its at.") 

的注意事项:在我的实践中,我没有发现太大的加速的预编译简单的正则表达式就像两个我使用,即使你正在使用它们数千次。重新模块可能有某种内部缓存 - 不费心读取源代码并进行检查。

此外,我替换了检查每个字符的方法,看看它是否在string.digits与另一个re.sub(),因为我认为我的版本更具可读性,并不是因为我确信它的性能更好(尽管可能)。

+0

这是约100倍更清晰 - 谢谢三联! – 2008-12-22 21:14:55

0

几件事情,将清理现有的正则表达式没有真正改变功能:

替换{0,1}用,[(]用([)用)。你也可以只是制作你的[2-9],因此你可以让这些模式成为最后一部分的\ d {3}和\ d {4}。我怀疑这会真的增加误报率。

0

为什么不重复使用别人的作品 - 例如,从RegExpLib.com

我的第二个建议是记住除美国之外还有其他国家,其中不少国家都有电话;-)请在我们的软件开发过程中不要忘记我们。

另外,还有格式化电话号码的标准;国际电联的E.123。我对标准的回忆是它所描述的与常用的用法不匹配。

编辑:我混淆了G.123和E.123。哎呀。道具Bortzmeyer

1

您的正则表达式仅解析特定的格式,这不是国际标准。如果你只限于一个国家,它可能会奏效。

否则,国际标准是ITU E.123:“符号为国内和国际电话号码, 电子邮件地址和网络地址”

1

首先,可靠地获取电话号码与一个正则表达式是出了名的难不可能的强烈倾向。并非每个国家都对“电话号码”的定义与美国一样窄,即使在美国也是如此。,事情复杂得多,他们似乎(从Wikipedia article on the North American Numbering Plan):

  • A)国家代码:可选的前缀( “1” 或 “1” 或 “001”)
    • ((00|\+)?1)?
  • B)编号计划区代码(NPA):不能从1开始,数字2不能是9
    • [2-9][0-8][0-9]
  • C)交换码(NXX):不能从1开始,不能以 “11” 结尾,可选圆括号
    • \(?[2-9](00|[2-9]{2})\)?
  • d)站代码:四位数字,不能全部为0 (我想)
    • (?!0{4})\d{4}
  • E)的可选扩展可遵循
    • ([x#-]\d+)?
  • S)的数量是空格,虚线,点(或不)
    • [. -]?

所以,基本的正则表达式分离对于美国将是:

((00|\+)?1[. -]?)?\(?[2-9][0-8][0-9]\)?[. -]?[2-9](00|[2-9]{2})[. -]?(?!0{4})\d{4}([. -]?[x#-]\d+)? 
| A  |S | | B    | S | C    | S | D   | S | E  | 

而这仅仅是美国相对微不足道的编号计划,即使在那里它也没有涵盖所有的细节。如果你想让它可靠,你必须为所有预期的输入语言开发一个类似的野兽。