2

我正在编写一段代码来将电话号码转换为手机的链接——我知道了,但感觉真的很脏。

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或类似的东西来代替我拥有的字符串黑客?

4

5 回答 5

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.")

注意:在我的实践中,我没有注意到像我正在使用的两个这样的简单正则表达式的预编译有太多的加速,即使你使用它们数千次。re 模块可能有某种内部缓存 - 没有费心阅读源代码和检查。

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

于 2008-12-22T19:26:03.587 回答
1

为什么不重复使用其他人的工作——例如来自RegExpLib.com的工作?

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

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

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

于 2008-12-22T06:37:28.287 回答
1

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

否则,国际标准是ITU E.123:“国家和国际电话号码、电子邮件地址和 Web 地址的符号”

于 2008-12-22T08:55:16.840 回答
1

首先,使用单个正则表达式可靠地捕获电话号码是出了名的困难,而且很可能是不可能的。并非每个国家/地区都有像美国一样狭窄的“电话号码”定义即使在美国,事情也比看起来更复杂(来自维基百科关于北美编号计划的文章):

  • 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      |

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

于 2008-12-22T09:41:01.817 回答
0

一些可以在不真正改变功能的情况下清理现有正则表达式的东西:

将 {0,1} 替换为 ?,将 [(] 替换为 (,[)] 替换为 )。你也可以把你的 [2-9] 设为 \d ,这样你就可以让这些模式成为最后一部分的 \d{3} 和 \d{4} 。我怀疑它真的会增加误报率。

于 2008-12-22T05:57:05.917 回答