[Python 实战] - No.12 Python 中的正则表达式使用(1)

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编程快速上手—让繁琐工作自动化》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章