您当前位置:首页 > 深入 Python > 正则表达式 > 案例研究:解析电话号码 | << >> | ||||
深入 Python从 Python 新手到专家 |
到目前为止,您已经专注于匹配整个模式。要么模式匹配,要么不匹配。但正则表达式的功能远不止于此。当正则表达式确实匹配时,您可以挑选出其中的特定部分。您可以找出匹配的位置。
这个例子来自我遇到的另一个现实问题,同样来自以前的工作。问题:解析美国电话号码。客户希望能够以自由格式输入号码(在单个字段中),但希望将区号、局号、号码和可选的分机号分别存储在公司的数据库中。我在网上搜索了许多声称可以做到这一点的正则表达式示例,但没有一个足够宽松。
以下是我需要能够接受的电话号码
种类繁多!在每种情况下,我都需要知道区号是 800,局号是 555,其余的电话号码是 1212。对于有分机号的电话,我需要知道分机号是 1234。
让我们逐步开发电话号码解析的解决方案。此示例显示了第一步。
>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})$')>>> phonePattern.search('800-555-1212').groups()
('800', '555', '1212') >>> phonePattern.search('800-555-1212-1234')
>>>
![]() |
始终从左到右阅读正则表达式。这个表达式匹配字符串的开头,然后是 (\d{3})。什么是 \d{3}?嗯,{3} 表示“匹配正好三个数字”;它是您之前看到的 {n,m} 语法 的一种变体。\d 表示“任何数字”(0 到 9)。将其放在括号中表示“匹配正好三个数字,然后将它们作为一个组记住,我以后可以要求”。然后匹配一个文字连字符。然后匹配另一组正好三个数字。然后是另一个文字连字符。然后是另一组正好四个数字。然后匹配字符串的结尾。 |
![]() |
要访问正则表达式解析器沿途记住的组,请对 search 函数返回的对象使用 groups() 方法。它将返回一个元组,其中包含在正则表达式中定义的组数。在这种情况下,您定义了三个组,一个包含三个数字,一个包含三个数字,一个包含四个数字。 |
![]() |
此正则表达式不是最终答案,因为它不处理末尾带有分机号的电话号码。为此,您需要扩展正则表达式。 |
>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})-(\d+)$')>>> phonePattern.search('800-555-1212-1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800 555 1212 1234')
>>> >>> phonePattern.search('800-555-1212')
>>>
下一个示例显示了用于处理电话号码不同部分之间的分隔符的正则表达式。
>>> phonePattern = re.compile(r'^(\d{3})\D+(\d{3})\D+(\d{4})\D+(\d+)$')>>> phonePattern.search('800 555 1212 1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212-1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('80055512121234')
>>> >>> phonePattern.search('800-555-1212')
>>>
下一个示例显示了用于处理没有分隔符的电话号码的正则表达式。
>>> phonePattern = re.compile(r'^(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')>>> phonePattern.search('80055512121234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800.555.1212 x1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212').groups()
('800', '555', '1212', '') >>> phonePattern.search('(800)5551212 x1234')
>>>
下一个示例显示了如何处理电话号码中的前导字符。
>>> phonePattern = re.compile(r'^\D*(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')>>> phonePattern.search('(800)5551212 ext. 1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212').groups()
('800', '555', '1212', '') >>> phonePattern.search('work 1-(800) 555.1212 #1234')
>>>
让我们退后一步。到目前为止,所有正则表达式都从字符串的开头开始匹配。但现在您看到字符串的开头可能有一些您想要忽略的不确定数量的内容。与其尝试将它们全部匹配,以便您可以跳过它们,不如采用不同的方法:根本不要显式匹配字符串的开头。下一个示例中显示了这种方法。
>>> phonePattern = re.compile(r'(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')>>> phonePattern.search('work 1-(800) 555.1212 #1234').groups()
('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212')
('800', '555', '1212', '') >>> phonePattern.search('80055512121234')
('800', '555', '1212', '1234')
看看正则表达式是如何快速失控的?快速浏览一下之前的任何一次迭代。你能说出其中一个和下一个之间的区别吗?
虽然您仍然理解最终答案(而且它就是最终答案;如果您发现了它无法处理的情况,我不想知道),但在您忘记做出选择的原因之前,让我们将其写成一个详细的正则表达式。
>>> phonePattern = re.compile(r''' # don't match beginning of string, number can start anywhere (\d{3}) # area code is 3 digits (e.g. '800') \D* # optional separator is any number of non-digits (\d{3}) # trunk is 3 digits (e.g. '555') \D* # optional separator (\d{4}) # rest of number is 4 digits (e.g. '1212') \D* # optional separator (\d*) # extension is optional and can be any number of digits $ # end of string ''', re.VERBOSE) >>> phonePattern.search('work 1-(800) 555.1212 #1234').groups()('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212')
('800', '555', '1212', '')
<< 详细正则表达式 |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
总结 >> |