Python中的re模块(第2节)


re模块

Python标准库中的re模块提供了正则表达式相关的操作,re模块提供了不少有用的函数,比如:re.match()函数、re.compile()函数、re.search()函数、re.findall()函数、re.finditer()函数、re.split()函数、re.sub()函数、re.subn()函数等。re模块使Python语言拥有全部的正则表达式功能。

在前面的教程中,我们已经讲解过,反斜杠“\”在字符串中用作转义字符,可以和其它字母形成转义字符,比如Python会识别字符串中“\n”转义字符并自动转换成“换行符”,所以想要表示一个反斜杠“\”就必须写成“\\”这种形式。但是在正则表达式中想要匹配字符“\”就必须写为“\\\\”,因为Python中的字符串解析器会把“\\\\”解析成“\\”,解析之后会再传递给正则表达式的解析器。由于正则表达式也有自己的语法结构,所以当它看到“\\”时,会把它解析为一个“\”,所以这时候正则匹配就会只匹配一个“\”。为了解决Python代码中的转义字符,最好的解决方法是,在字符串前面加上“r”前缀。“r”前缀的主要作用是将一个字符串标记为原始字符串,让字符串中的所有字符都代表自己,不会出现转义符。例如:

动手练一练:

print("\\\\")  # 输出 \\
print("\\")  # 输出 \
print(r"\\")  # 输出 \\
print("\n")  # 输出 一个换行符
print(r"\n")  # 输出 “\n”字符而不是换行符

执行以上代码,输出结果为:

\\
\
\\

\n

上面的例子中添加“r”前缀后,对于处理文件路径非常有用,因为它允许你编写包含反斜杠“\”的路径而不必担心反斜杠被误解。

1、re.compile()函数

正则表达式是一种强大的文本模式匹配工具,它在Python中通过re模块提供支持。re.compile()函数是re模块中的一个重要函数,它可以将一个字符串形式的正则表达式编译为一个正则表达式对象,该对象可以用于后续的匹配操作,并且可以被多次使用。通常情况下,使用re.compile()函数可以提高正则表达式的执行效率。例如:

动手练一练:

import re

text = "a,,b,,,,c, ,d"
result = re.compile('[, ]+')  # 匹配逗号或者空格一次或多次
print(result.split(text))  # split()函数根据正则表达式匹配的结果将字符串进行分割,返回一个列表

执行以上代码,输出结果为:

['a', 'b', 'c', 'd']

上面的例子中,我们使用re.compile()函数编译正则表达式,并生成一个对象。然后,我们通过使用result对象进行匹配操作,并调用split()函数根据正则表达式匹配的结果将字符串进行分割,返回一个列表。其中,正则表达式匹配的结果是逗号和空格,split()函数根据匹配到的逗号和空格当作分界线,将字符串分割成4个字母:a、b、c、d。

2、re.match()函数

re.match()函数尝试从起始位置开始匹配一个符合规则的字符串,如果未指定位置,默认从第一个位置开始匹配,匹配成功返回一个对象,未匹配成功返回None。

re.match()函数的语法如下所示:

re.match(pattern, string, flags=0)
# pattern  匹配的正则表达式
# string  要匹配的字符串
# flags  用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等
# 其中第三个参数flags可以省略不写

re.match()函数的第三个参数flags为可选参数,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:

re.I(re.IGNORECASE)忽略大小写括号内是完整的写法
re.M(MULTILINE)多行匹配模式改变^“$”的行为
re.S(DOTALL):“.可以匹配任意字符包括换行符改变.的行为
re.L(LOCALE)做本地化识别的匹配表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境不推荐使用
re.U(UNICODE)使用\w \W \b \B \s \S \d \D使用取决于unicode定义的字符属性
re.X(VERBOSE)详细模式这个模式下正则表达式可以是多行忽略空白字符并可以加入注释

下面是re.match()函数匹配字符串的简单例子:

动手练一练:

import re

a1 = re.match(r"www", "www.pyhint.com", re.I) # re.I忽略大小写模式
print("匹配的内容:", a1.group()) # group()函数提取数据,输出“www”
print("匹配的位置:", a1.span())  # span()函数获得匹配结果的位置为:(0, 3)

# 下面匹配不成功,因为第二个参数开头没有“com”。
a2 = re.match(r"com", "www.pyhint.com", re.I)
if a2:
    print(a2.group())
else:
    print("a2中没匹配到相关内容")  # 第二个参数开头没有"com",输出None

# 下面匹配不成功,因为第二个参数开头没有数字。
a3 = re.match(r"\d+", "python456")  # 匹配至少一个数字
if a3:
    print(a3.group())
else:
    print("a3中没匹配到相关内容") # 第二个参数开头没有数字,输出None

a4 = re.match(r"[a-z]+", "EfgHi", re.I) # 匹配至少一个字母,re.I忽略大小写模式
print(a4.group())  # 输出 EfgHi

a5 = re.match("..","pythonsfcv") # 使用两个点号就代表匹配两个字符
print(a5.group())   #输出py

a6 = re.match("\d\d","68dgd96rg")  # 一个\d代表匹配一个数字
print(a6.group())   #输出68

a7 = re.match("\D\D","python68dgd")  # 一个\D代表匹配一个非数字
print(a7.group())   #输出py

a8 = re.match("\s"," python68dgd")  # \s匹配特殊字符,如空白,空格,tab等
print(a8.group())   #输出空格

a9 = re.match("\S\S","python68dgd")  # \S\S匹配两个非空白字符
print(a9.group())   #输出py

a10 = re.match("\w\w\w","Py_thon68dgd")  # \w匹配大小写字母、数字或_下划线
print(a10.group())   #输出Py_

a10 = re.match("\W\W\W","   python68dgd")  # \W匹配非单词字符
print(a10.group())   #输出3个空格

a11 = re.match("12[34]","123python68dgd")  # [ ]匹配方括号[ ]中列举的任意1个字符
print(a11.group())   #输出123

a12 = re.match("12[^34]","125python68dgd")  # [^34]不匹配34中的任意1个字符
print(a12.group())   #输出125

a13 = re.match("12[4-6m-q]","12python68dgd")  # [4-6m-q]匹配4到6或者m到q中的字符
print(a13.group())   #输出12p

a14 = re.match(".*","python68dgd")  # “.*”匹配任意1个字符出现0次或无数次
print(a14.group())   #输出python68dgd

a15 = re.match("p*","pppython68dgd")  # 匹配p字母零次或多次
print(a15.group())   #输出ppp

a16 = re.match("p\d*","python68dgd")  # \d*匹配零次或多次数字字符
print(a16.group())   #输出p, 因为*也可以代表0次

a17 = re.match("p+","ppython68dgd")  # p+匹配p至少出现一次
print(a17.group())   #输出pp

a18 = re.match("p?","ython68dgd")  # p?匹配p出现0次或者1次数
print(a18.group())   #输出空格,因为p可以为0次

a19 = re.match("py{2}","pyyython68dgd")  #匹配p以及跟随在后面的两个yy
print(a19.group())   #输出pyy

a20 = re.match("py{2,}","pyyython68dgd")  #匹配p以及跟随在后面的三个yyy至少出现2次
print(a20.group())   #输出pyyy

a21 = re.match("py{2,3}","pyyython68dgd")  #{2,3}指定从2到3次的范围
print(a21.group())   #输出pyyy

a22 = re.match(".*d$","python68dgd")   #字符串必须以d结尾 
print(a22.group())   #输出python68dgd

a23 = re.match("^p","python68dgd")   #规定必须以p开头
print(a23.group())   #输出p

a24 = re.match(r"py\b","py.thon68dgd") # \b匹配py作为单词边界,前面必须加上r消除转义
# py的右边不能有字母和数字
print(a24.group())   #输出py

a25 = re.match(r"py\B","python68dgd") # \B匹配py作为非单词边界
# py的右边必须有字母和数字
print(a25.group())   #输出py

a26 = re.match(r"\d[5-9]|\D[a-z]","68pythondgd") #匹配“|”两边任意一个表达式
print(a26.group())   # \d匹配“6”,[5-9]匹配“8”,所以输出68

a27 = re.match(r"<h1>(.*)<h1>","<h1>你好!<h1>") #会将()中的内容会作为一个元组字符装在元组中
print(a27.group())   #输出“<h1>你好!<h1>”
print(a27.groups())  #输出“('你好!',)”

上面的例子中,re.match()函数返回一个匹配的对象,而不是匹配的内容。如果想要提取数据则需要调用group()函数,并且返回全部匹配对象,而groups()函数返回一个包含唯一或者所有子组的元组,如果正则表达式中没有子组的话,groups()将返回一个空元组。通过调用span()可以获得匹配结果的位置。如果从起始位置开始没有匹配成功,即便是其他部分包含匹配的内容,re.match()也会返回None。

也可以使用compile()函数生成正则表达式对象,再通过正则表达式对象中的match()方法实现同样的功能,例如:

动手练一练:

import re

result = re.compile(r'\d+')  #匹配至少一个数字
a1 = result.match('py789')   #未指定位置,默认从第一个字符开始匹配
print(a1)                    #第一个字符没有匹配到数字,输出None
a2 = result.match('py789', 2, 4) #匹配从位置2到位置4的字符               
print(a2.group())            #输出78

# 直接使用re.match()函数匹配
a3 = re.match(r'\d+','py789')
print(a3)                    #输出None

# 匹配至少一个字母,并且忽略大小写
a4 = re.match(r'[a-z]+','EfgHi',re.I) #re.I忽略大小写模式
print(a4.group())            #输出 EfgHi

执行以上代码,输出结果为:

None
78
None
EfgHi

3、re.search()函数

re.search()函数re.match()函数用法差不多,re.search()函数会在整个字符串内查找匹配,同样也只匹配一次,返回一个结果,如果字符串没有匹配成功,则返回None。这里需要注意的是,re.search()函数只找到第一个符合条件的字符串就返回了,所以即使被搜索的字符串中还有其它符合的字符串,也将被忽略。

re.match()函数与re.search()的区别:re.match()函数只匹配字符串的开头位置,如果正则表达式在开头位置匹配成功才能返回,re.search()函数则在整个字符串中搜索匹配项,找到第一个匹配项后才能返回。例如:

动手练一练:

import re

result = re.compile(r"\d+")  #匹配至少一个数字
a1 = result.search("py123dgv456")   #未指定位置,默认匹配整个字符串
print(a1.group())                   #匹配第一个符合条件的字符串“123”
a2 = result.search("py123dgv456", 2, 4) #匹配从位置2到位置4的字符               
print(a2.group())            #输出 12

# 直接使用re.search()函数匹配
a3 = re.search(r"\d+","py123dgv456")
print(a3.group())            #输出 123

# 匹配至少一个字母,并且忽略大小写
a4 = re.search(r"[a-z]+","123Py456Ef",re.I) #re.I忽略大小写模式
print(a4.group())            #输出 Py

执行以上代码,输出结果为:

123
12
123
Py

4、re.findall()函数

re.search()函数re.match()函数都只能匹配一次,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果。然而,在大多数时候,我们需要搜索整个字符串,获得所有匹配的结果,这个时候就必须用到re.findall()函数

从字面意思上就可以看到,findall是由find(查找)和all(所有)组成,因此re.findall()函数可以获取全部的匹配结果,但是re.findall()函数并不会返回None,不管有没有结果,都会返回一个列表。例如:

动手练一练:

import re

result = re.compile(r"\d+")  #匹配至少一个数字
a1 = result.findall("py123dgv456")   #未指定位置,默认匹配整个字符串
print(a1)            #输出 ['123', '456']
a2 = result.findall("py123dgv456", 2, 4) #匹配从位置2到位置4的字符               
print(a2)            #输出 ['12']

# 直接使用re.findall函数匹配
a3 = re.findall(r"\d+", "py123dgv456")
print(a3)            #输出 ['123', '456']

# 匹配至少一个字母,并且忽略大小写
a4 = re.findall(r"[a-z]+", "123Py456Ef", re.I) #re.I忽略大小写模式
print(a4)            #输出 ['Py', 'Ef']

执行以上代码,输出结果为:

['123', '456']
['12']
['123', '456']
['Py', 'Ef']

5、re.split()函数

re.split()函数是根据正则表达式匹配的结果将字符串进行分割,返回值是切割后的内容列表,跟直接使用字符串的split()函数用法类似。例如:

动手练一练:

import re

result = re.compile(r"[\,\;\s]+")  #匹配“,”、“;”和空格
a1 = result.split("p,y;t ;h o;;n")
print(a1)    #输出 ['p', 'y', 't', 'h', 'o', 'n']
#输出 ['123', '456']

a2 = re.split(r"[\,\;\s]+", "p,y;t ;h o;;n")          
print(a2)    #输出 ['p', 'y', 't', 'h', 'o', 'n']

执行以上代码,输出结果为:

['p', 'y', 't', 'h', 'o', 'n']
['p', 'y', 't', 'h', 'o', 'n']

6、re.sub()函数

re模块中的re.sub()函数是Python中一个非常强大的正则表达式替换函数,它可以在字符串中通过正则表达式匹配的方式搜索指定的字符串,并将匹配的字符串替换成指定的内容。例如:

动手练一练:

import re

a = "我的名字叫张三,我今年18岁,我的银行卡密码是123456"
result = re.compile(r"(\d+)")  #匹配至少一个数字
a1 = result.sub("**", a)   #用“**”替换“18”和“123456”
print(a1) #输出 我的名字叫张三,我今年**岁,我的银行卡密码是**

a2 = result.sub("**", a, 1)   #只替换一次
print(a2) #输出 我的名字叫张三,我今年**岁,我的银行卡密码是123456

# 直接使用re.sub函数替换
a3 = re.sub(r"\d+", "**", a)
print(a3) #输出 我的名字叫张三,我今年**岁,我的银行卡密码是**

# 直接使用re.sub函数替换一次
a3 = re.sub(r"\d+", "**", a, 1)   #只替换一次
print(a3) #输出 我的名字叫张三,我今年**岁,我的银行卡密码是123456

执行以上代码,输出结果为:

我的名字叫张三我今年**我的银行卡密码是**
我的名字叫张三我今年**我的银行卡密码是123456
我的名字叫张三我今年**我的银行卡密码是**
我的名字叫张三我今年**我的银行卡密码是123456