第七章 模式匹配和正则表达式
1.不用正则表达式来查找文本模式
#对于这样的一个文本查找:3个数字,一个短横线,3个数字,4个端横线,然后再是4个数字,如:415-555-4242
def isPhoneNumber(text): if len(text)!=12: return False for i in range(0,3): if not text[i].isdecimal(): return False if text[3]!='-': return False for i in range(4,7): if not text[i].isdecimal(): return False if text[7]!='-': return False for i in range(8,12): if not text[i].isdecimal(): return False return True print(isPhoneNumber('444-555-6767'))
这个地方isPhoneNumber()函数对代码进行几行的检查,首先检查字符串是不是刚好12个字符,然后检查前3个字符,
2.用正则表达式查找文本模式
(1)正则表达式,简称为regex,是文本模式的描述方法。如\d是一个正则表达式,表示的是一位数字的字符,即任何0-9之间的数字。python使用正则表达式\d\d\d-\d\d\d-\d\d\d\d,来匹配isPhoneNumber()函数匹配同样的文本:3个数字 、1个横线、3个数字、一个短横线、4个数字。所有的其他字符串都不能匹配\d\d\d-\d\d\d-\d\d\d\d的正则表达式
在一个模式的后面加上一个花括号包围的3({3}),也就是匹配这个模式3次。所以正则表达式\d{3}-\d{3}-\d{4}也在匹配正确的电话号码匹配格式
(2)python中所有的正则表达式的函数都在re模块中。在交互式环境中,导入该模块
import re
在本章的后面大多数的例子都需要用到re模块,所以要记得在每个脚本开始的地方导入它
向re.compile()传入一个字符串的值,表示正则表达式,将会返回Regix模式对象
在交互环境中创建一个Regix对象来匹配电话号码模式,在交互环境中的代码如下:
phoneNumRegix=re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
(3)匹配Regix对象
>>> import re >>> phoneNumRegix=re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') >>> mo=phoneNumRegix.search('My number is 415-555-4343.') >>> print('Phone number found:'+mo.group()) Phone number found:415-555-4343
regix对象search方法查找传入的字符串,寻找正则表达式所有的匹配。如果字符串中没有找到该正则表达式的表达式模式,searchI()方法将会返回None。如果找到了该模式,search()方法将会返回一个Match对象。Match对象有一个group()方法,它返回被查找字符串中实际匹配的文本
这里现将期待的模式传递给re.complie(),并得到Regix对象保存在phoneNumRegix中。然后我们在phoneNumRegix上调用search(),向其传入向查找的字符串,将查找的结果保存在mo中
(4)正则表达式的复习
用import re 导入正则表达式模块
用re.compile()函数创建一个Regix对象
向Regix对象的search()方法传入想要查找的字符串,返回实际匹配的文本字符串
调用Match对象的group()方法,返回实际匹配文本的字符串
(5)用正则表达式匹配更多的模式
利用括号进行分组。假设要将区号从电话号码中分离。添加括号在正则表达式中创建'分组:'(\d\d\d)-(\d\d\d-\d\d\d\d)。然后可以用group()匹配对象的方法,从一个分组中获取匹配的文本。在正则表达式字符串中,第一个括号就是第一组,第二个括号就是第二组。向group()匹配对象传入整数1或者整数2,就可以取得匹配文本的不同的部分。向group()方法传入0或者不传入参数,将返回整个匹配的文本。
>>> phoneNumRegix=re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') >>> mo=phoneNumRegix.search('My number is 415-555-4242') >>> mo.group(1) '415' >>> mo.group(2) '555-4242' >>> mo.group(2) '555-4242' >>> mo.group() '415-555-4242'
#如果想一次就获取所有的分组,可以使用groups()方法
>>> mo.groups()
('415', '555-4242')
>>> areaCode,mainNumber=mo.groups()
>>> print(areaCode)
415
>>> print(mainNumber)
555-4242
这个地方可以发现,mo.groups()返回的是多个值的元组,所以可以使用多重复制的技巧,每个值赋给一个变量
(7)用管道来匹配多个分组
字符|称之为"管道"。希望匹配许多表达式中的一个的时候,就可以使用这个。例如:正则表达式r'Batman|Tina Fey'将会匹配'Batman'或者'Tina Fey'。如果Batman和Tina Fey都出现在被查找的字符串中,第一次次出现的Match对象返回。
>>> heroRegex=re.compile(r'Batman|Tina Fey') >>> mo1=heroRegix.search('Batman and Tina Fey.') Traceback (most recent call last): File "", line 1, in NameError: name 'heroRegix' is not defined >>> mo1=heroRegex.search('Batman and Tina Fey.') >>> mo1.group() 'Batman' >>> mo2=heroRegex.search('Tina Fey and Batman.') >>> mo2.group() 'Tina Fey'
利用findall()方法,可以找到所有匹配的地方。也可以使用管道来匹配多个模式中的一个,作为正则表达式的一部分。例如:假设你希望匹配'Batman'、'Batmobile','Batcopter'和'Batbat'中的任意一个。因为所有这些字符串都以Bat开始,所以如果能够指定一次前缀,就会很方便,可以通过括号实现。
>>> batRegex=re.compile(r'Bat(man|mobile|copter|bat)') >>> mo=batRegex.search('Batmobile lost a wheel') >>> mo.group() 'Batmobile' >>> mo.group(1) 'mobile' >>>
方法调用mo.group()返回了完全匹配的文本'Batmobile',而mo.group(1)只是返回第一个括号分组内匹配的文本'mobile'。通过管道分组和分组括号