1. Python中如何使用正则表达式
Python中使用正则表达式的步骤如下:
- 使用
import re
导入正则表达式模块 - 使用
re.compile()
创建一个对象 - 使用
Regex
对象的search()
方法,传入一个字符串,然后返回一个Match
对象 - 调用
Match
对象的group()
方法,返回文本中匹配该正则表达式的字符串
示例如下,查找学生姓名中姓Zhang
的同学姓名
import re
namelist = "Li Ming;Zhang San;Fu yu;Guo Ji;Ren Jie;Zhang Lin;"
nameRegex = re.compile(r"Zhang\s\w+")
match = nameRegex.search(namelist)
print(match.group())
结果如下:
Zhang San
上面的代码中,有几个地方需要解释一下:
re.compile(r"Zhang San\s\w+")
在正则表达式的前面加了一个r,标识该字符为原始字符串。因为,在Python中,转义字符前面需要加\
来标记,如果你需要在字符串中打出\
,那么你需要使用\\
,或者在字符串的前面加入一个r
来标记r"Zhang San\s\w+"
和"Zhang San\\s\\w+"
是等价的search()
函数匹配文本中第一个符合该字符串的结果并返回一个Match
对象,Match
对象的group()
函数将返回被查找到的实际文本。所以在上述结果中,我们仅得到Zhang San
这个结果。如果你的正则表达式中含有分组(后续会讲到),你可以使用group(1)
,group(2)
来查询正则表达式中第一个,第二个分组的匹配结果。
2. 正则表达式的更多模式
1. 使用括号分组
假设,某地区的电话号码的表示形式为123-456-7890
的形式,且前三位为区号,后七位标识电话号,要求将从文本中同时获取区号,电话号和整体的电话号码。
代码和结果如下:
text = "My phone number is 455-789-1234"
pnRegex = re.compile(r"(\d\d\d)-(\d\d\d-\d\d\d\d)")
match = pnRegex.search(text)
print(match.group())
print(match.group(1))
print(match.group(2))
print(match.groups())
结果如下:
455-789-1234
455
789-1234
(‘455’, ‘789-1234’)
group()
默认传入参数为0,即返回整个匹配的文本。如果想获取全体分组的结果,使用groups()
函数,该函数返回一个包含所有分组匹配结果的元组。
2. 使用管道匹配多个分组
字符|
是正则表达式中的管道,用来匹配许多表达式中的一个。如果想匹配姓名列表中,姓Zhang的和姓Li的同学的姓名,可以使用管道|
来连接多个正则表达式。
namelist1 = "Li Ming;Zhang San;Fu yu;Guo Ji;Ren Jie;Zhang Lin;"
namelist2 = "Zhang San;Fu yu;Guo Ji;Ren Jie;Zhang Lin;Li Ming;"
nameRegex = re.compile(r"Zhang\s\w+|Li\s\w+")
match1 = nameRegex.search(namelist1)
print(match1.group())
match2 = nameRegex.search(namelist2)
print(match2.group())
结果如下:
Li Ming
Zhang San
3. 使用问号实现可选匹配
例如在之前的电话匹配中,我们希望即便有人省略区号,依然可以检测出电话号码。使用?
来实现部分匹配的模式是可选的
text1 = "My phone number is 455-789-1234"
text2 = "My phone number is 789-1234"
pnRegex = re.compile(r"(\d\d\d-)?(\d\d\d-\d\d\d\d)")
match1 = pnRegex.search(text1)
print(match1.group())
match2 = pnRegex.search(text2)
print(match2.group())
结果如下:
455-789-1234
789-1234
4. 使用花括号匹配特定次数
假设现在我们有一串字符串:
* *** ********** ** *** ****** ** ***** * ******* ***** **** ***** * *** * **
如果我们想匹配一下几种情况:
- 恰好三个* 连在一起的,如
***
- 少于等于三个* 连在一起的,如
**
,*
- 连在一起的*个数大于等于四,但是小于等于五
- 大于等于六个*连在一起的,如
******
代码如下:
text = "* *** ********** ** **** ****** ** ***** * ******* ***** **** ***** * *** * **"
sRegex1 = re.compile(r"(\*){3}")
sRegex2 = re.compile(r"(\*){,3}")
sRegex3 = re.compile(r"(\*){4,5}")
sRegex4 = re.compile(r"(\*){6,}")
match1 = sRegex1.search(text)
match2 = sRegex2.search(text)
match3 = sRegex3.search(text)
match4 = sRegex4.search(text)
print(match1.group())
print(match2.group())
print(match3.group())
print(match4.group())
结果如下所示:
***
*
*****
**********
r"(\*){3}"
中,(\*)
表示匹配型字符的分组。因为 在正则表达式中表示匹配一个或多个,所以需要使用\
进行转义,表示字符 *
花括号{n,m}
,表示前面的分组重复次数大于等于n次并且小于等于m次。m和n也可省略其中一个,表示大于等于n或者小于等于m。{n}
表示分组恰好重复n次。
另外,可以看到,在被匹配的文本中,长度为4的字符串****
排在长度为5的字符串*****
前面,但是代码查找到的结果是*****
,这是因为默认情况下正则表达式是贪婪地,花括号的贪婪版本会尽可能的匹配更长的字符串。使用字符?
可以声明正则表达式为非贪心形式
text = "* *** ********** ** *** ****** **** ***** * ******* ***** **** ***** * *** * **"
sRegex1 = re.compile(r"(\*){4,5}")
match1 = sRegex1.search(text)
sRegex2 = re.compile(r"(\*){4,5}?")
match2 = sRegex2.search(text)
print(match1.group())
print(match2.group())
结果如下:
*****
****
5. findall()方法
re模块的findall()
方法返回被匹配文本中的所有匹配到的结果。
之前提到的search()
仅返回文本中第一个匹配到的结果,方法返回一个Match
对象,并调用Match对象的group()函数获取匹配结果
findall()
匹配文本中所有匹配的结果,并且返回一个所有结果的列表。如果正则表达式中有分组,那么findall()
将返回分组的列表
比如之前的电话号的正则表达式:
text = "My phone number is 455-789-1234,Lily's phone number is 110-101-1230 and Lucy's phone number is 789-456-1245"
pnRegex = re.compile(r"(\d\d\d)-(\d\d\d-\d\d\d\d)")
reslist = pnRegex.findall(text)
print(reslist)
结果如下:
[(‘455’, ‘789-1234’), (‘110’, ‘101-1230’), (‘789’, ‘456-1245’)]
正则表达式中的常用字符表,网上资源很多,这里不再放出来。
P.S. 文章不足之处还望指正
参考书籍:《Python编程快速上手—让繁琐工作自动化》