Python正则表达式re模块详解:从入门到精通掌握字符匹配技巧
[id_18752[id_[id_1593872924]7498159]896]

正则表达式本身是一种小型且高度专业化的编程语言。在 Python 中,通过内嵌集成 re 模块,程序媛们能够直接调用该模块来实现正则匹配。正则表达式模式会被编译成一系列字节码,接着由用 C 编写的匹配引擎来执行。
二、正则表达式中常用的字符含义
1、普通字符和11个元字符:
普通字符
匹配自身
abc
abc
匹配除换行符"\n"之外的任意字符,并且在 DOTALL 模式下也能匹配换行符。
a.c
abc
转义字符,使后一个字符改变原来的意思
a\.c;a\\c
a.c;a\c
匹配前一个字符0或多次
abc*
ab;abccc
匹配前一个字符1次或无限次
abc+
abc;abccc
[id_1160689407]
ab;abc
匹配字符串开头。在多行模式中匹配每一行的开头
^abc
abc
匹配字符串末尾,在多行模式中匹配每一行的末尾
abc$
abc
匹配左右表达式中的任意一个,从左至右进行匹配。如果某个元素没有被包含在括号中,那么它的范围就是整个正则表达式。
abc|def
abc
def
{}
{m}表示匹配前一个字符 m 次,{m,n}表示匹配前一个字符 m 至 n 次,若未给出 n ,则匹配 m 至无限次。
ab{1,2}c
abc
abbc
[]
字符集。其对应的位置能够是字符集中的任意字符。字符集中的字符既可以逐个进行罗列,也可以给出范围,例如[abc]或者[a - c]。而[^abc]表示取反,也就是非 abc。
所有特殊字符在字符集中都不再具有其原本的特殊含义。通过使用\反斜杠进行转义操作,能够使特殊字符恢复其特殊含义。
a[bcd]e
abe
ace
ade
()
分组将被括起来,从表达式的左边开始,每当没遇到一个分组的左括号“(”时,编号就会加 1.
分组表达式可作为一个整体,其后可接数量词。在表达式中,|仅在该组内有效。
(abc){2}
a(123|456)c
abcabc
a456c
这里需要强调一下反斜杠\的作用:
使用 re 模块的 search 方法在字符串'tinafeihahafei tinafeihahatina'中搜索模式'(tina)(fei)haha\2',找到匹配的内容后,通过 group 方法获取匹配的结果,最终结果赋值给变量 a 。 print(a) 结果: tinafeihahafei
2、预定义字符集(可以写在字符集[...]中)
\d
数字:[0-9]
a\bc
a1c
\D
非数字:[^\d]
a\Dc
abc
\s
匹配任何空白字符:[\t\r\n\f\v]
a\sc
a c
\S
非空白字符:[^\s]
a\Sc
abc
\w
匹配包括下划线在内的任何字字符:[A-Za-z0-9_]
a\wc
abc
\W
匹配非字母字符,即匹配特殊字符
a\Wc
a c
\A
仅匹配字符串开头,同^
\Aabc
abc
\Z
仅匹配字符串结尾,同$
abc\Z
abc
\b
在\w和\W之间进行匹配,这意味着匹配单词边界。单词边界指的是单词与空格之间的位置。例如,“er\b”能够匹配“never”中的“er”,然而却不能匹配“verb”中的“er”。
\babc\b
a\b!bc
空格abc空格
a!bc
\B
[^\b]
a\Bbc
abc
这里需要强调一下\b的单词边界的理解: w 等于从“tian tinaaaa”中找出所有包含“tina”的内容。 print(w) 在字符串“tian tinaaaa”中,使用 re.findall 函数去查找所有包含“tina”的部分,结果存储在变量 s 中。 print(s) v 从 'tian#tinaaaa' 中找出所有包含 'tina' 的内容。 print(v) 使用正则表达式 r'\btina\b' 在字符串 'tian#tina@aaa' 中进行查找,找到的结果赋值给变量 a ,即 a 等于在该字符串中找到的所有匹配 'tina' 的部分。 print(a) 执行结果如下: [] ['tina'] ['tina'] ['tina']
3、特殊分组用法:
分组,除了原有的编号外再指定一个额外的别名
(?Pabc){2}
abcabc
(?P=name)
引用别名为的分组匹配到字符串
(?P\d)abc(?P=id)
1abc1
5abc5
引用编号为的分组匹配到字符串
(\d)abc\1
1abc1
5abc5
三、re模块中常用功能函数
1、compile()
编译正则表达式模式,会返回一个对象的模式。可以将常用的正则表达式编译为正则表达式对象,以此能提高一定的效率。
格式:
re.compile(pattern,flags=0)
pattern: 编译时用的表达式字符串。
flags 是编译标志位,它可用来修改正则表达式的匹配方式,例如是否区分大小写以及多行匹配等。常用的 flags 包含:
标志
含义
re.S(DOTALL)
使.匹配包括换行在内的所有字符
re.I(IGNORECASE)
使匹配对大小写不敏感
re.L(LOCALE)
做本地化识别(locale-aware)匹配,法语等
re.M(MULTILINE)
多行匹配,影响^和$
re.X(VERBOSE)
该标志通过给予更灵活的格式以便将正则表达式写得更易于理解
re.U
从 Unicode 字符集来对字符进行解析,此标志会对\w、\W、\b、\B 产生影响。
import re rr 是通过 re.compile 函数编译得到的,编译的内容是 '\\w*oo\\w*' 。 查找包含“oo”的单词,通过 rr.findall(tt) 来实现 执行结果如下: “good”和“cool”
2、match()
决定 RE 是否在字符串的起始位置进行匹配。此方法并非完全匹配,当 pattern 结束而 string 还有剩余字符时,仍视为成功。若要完全匹配,可在表达式末尾添加边界匹配符“$”。
格式:
使用 re.match 函数,传入 pattern 和 string 作为参数,同时可以设置 flags 为 0 来进行匹配操作。
最终打印出匹配到的内容 使用 re.match 函数来匹配字符串 'Comwww.runcomoob' 中是否包含 'com' ,忽略大小写(re.I),然后获取匹配到的内容并输出。具体来说,就是在给定的字符串中查找以 'com' 开头的部分,并将其作为匹配结果输出。如果没有找到匹配的内容,则返回 None。 执行结果如下: com com
3、search()
格式:
在字符串 string 中搜索模式 pattern ,标志位 flags 为 0 。 它用于查找字符串中与给定模式匹配的部分。 该函数会返回一个匹配对象,如果没有找到匹配项,则返回 None 。 可以通过匹配对象的方法来获取关于匹配的详细信息。 例如,使用 group() 方法可以获取匹配的文本。
re.search 函数会在字符串内进行查找模式匹配的操作。它只要找到第一个匹配就会返回。如果字符串没有匹配的情况,就会返回 None。
最后输出匹配到的内容。 执行结果如下: 4com
match 和 search 一旦匹配成功,就会形成一个 match object 对象,这个 match object 对象具备以下这些方法:
a. group()返回re整体匹配的字符串,
group 函数,其参数为 n 和 m,该函数会返回组号为 n 且 m 所匹配的字符串。倘若组号不存在,就会抛出 indexError 异常。
groups()方法会返回一个包含正则表达式中所有小组字符串的元组,其范围是从 1 到所包含的小组号。通常情况下,groups()不需要参数,它会返回一个元组,该元组中的元素就是正则表达式中定义的组。
import re a = "123abc456" 如果在字符串 a 中搜索到符合 "([0-9]*)([a-z]*)([0-9]*)" 模式的内容,那么就打印出通过 re.search 找到的匹配项的 group(0),即整体内容。例如对于 "123abc456" 这样的字符串,就会返回 "123abc456" 。 在字符串 a 中搜索符合 "([0-9]*)([a-z]*)([0-9]*)" 模式的内容,然后获取匹配到的第一个分组的内容,即数字部分。 使用正则表达式在字符串 a 中进行搜索,查找符合 "([0-9]*)([a-z]*)([0-9]*)" 模式的内容,然后获取匹配到的第二个分组的内容并打印出来,这里的第二个分组是由小写字母组成的部分,例如在字符串 "abc" 中,获取到的就是 "bc" 中的 "bc" 部分。 在 a 中搜索匹配 "([0-9]*)([a-z]*)([0-9]*)" 的模式,然后打印出匹配到的第三个分组,即数字部分,这里的数字部分是 456 ###group(3) 列出第三个括号匹配的内容。###
4、findall()
re.findall 进行遍历匹配操作,能够获取到字符串里所有匹配的字符串,并且会返回一个列表。
格式:
在字符串 string 中查找与 pattern 匹配的所有子串,使用的标志为 flags=0 。
使用 re 模块的 compile 函数创建了一个正则表达式模式对象 p,该模式是 r'\d+',即匹配一个或多个数字。 使用 p 来查找符合 'o1n2m3k4' 的内容并进行打印。 执行结果如下: 有四个元素,分别是 '1'、'2'、'3'、'4'。
import re tt = "Tina is a good girl, she is cool, clever, and so on..." rr = re.compile(r'\w*oo\w*') print(rr.findall(tt)) 使用 re.findall 函数来查找字符串 tt 中所有包含 'oo' 的子串。其中,正则表达式 '(\w)*oo(\w)' 表示匹配任意数量的字母字符(包括空字符),接着是 'oo',最后再匹配任意一个字母字符。通过这种方式,可以找到 tt 中所有符合该模式的子串。 执行结果如下: ['good', 'cool']
5、finditer()
搜索 string 后,会返回一个迭代器,该迭代器可以顺序访问每一个匹配结果,这些匹配结果是 Match 对象。同时,它能找到 RE 匹配的所有子串,并将这些子串作为一个迭代器返回。
格式:
在字符串 string 中查找与 pattern 匹配的所有子串,使用的标志为 flags=0 ,通过 re.finditer 函数来实现。
iter 是在字符串 '12 drumm44ers drumming, 11... 10...' 中查找匹配正则表达式 '\d+' 的迭代器
for i in iter:
print(i)
print(i.group())
print(i.span())
执行结果如下:
<_sre.SRE_Match object; span=(0, 2), match='12'>
12
(0, 2)
<_sre.SRE_Match object; span=(8, 10), match='44'>
44
(8, 10)
<_sre.SRE_Match object; span=(24, 26), match='11'>
11
(24, 26)
<_sre.SRE_Match object; span=(31, 33), match='10'>
10
(31, 33)
6、split()
按照能够匹配的子串将string分割后返回列表。
可以利用 re.split 来对字符串进行分割,例如:re.split(r'\s+', text);这样就能把字符串按照空格分割成一个单词的列表。
格式:
re 按照 pattern 对 string 进行分割操作,分割次数可以由 maxsplit 指定,如果不指定 maxsplit,则默认分割所有匹配的部分。
maxsplit用于指定最大分割次数,不指定将全部分割。
将分割后的结果打印出来 执行结果如下: 有 'one'、有 'two'、有 'three'、有 'four'、有 'five' 。
7、sub()
将 string 中每一个匹配的子串用 re 进行替换,然后返回替换后的字符串。
格式:
使用 re.sub 函数,传入 pattern、repl、string 和 count 参数,对 string 进行替换操作,pattern 是要匹配的模式,repl 是替换后的内容,count 是替换的次数(可选)。
import re 使用 re.sub 函数,将字符串 text 中的连续空白字符替换为“-”,然后打印替换后的结果。 执行结果如下: JGood 是一个帅气的男孩,他很酷炫,很聪明,等等……
其中第二个函数是被替换后的字符串;在本例中这个字符串是'-'
第四个参数指替换个数。默认为0,表示每个匹配项都替换。
re.sub还允许使用函数对匹配项的替换进行复杂的处理。
使用 re.sub 函数,将字符串中的空格(r'\s')替换为 [空格内容],这里的空格内容是通过 lambda 函数 m 获取到的原始空格(m.group(0)),并将替换结果应用到指定的字符串 text 中,0 表示不进行全局替换,只进行第一次替换。
import re text = "JGood is a handsome boy, he is cool, clever, and so on..." 使用 re.sub 函数,将字符串 text 中连续的空白字符(\s+)替换为 [原空白字符],并设置标志位为 0,最后打印替换后的结果。 执行结果如下: JGood 是一个帅气的男孩,他很冷酷,很聪明,等等……
8、subn()
返回替换次数
格式:
subn 函数用于在字符串中进行模式匹配和替换操作。它接受模式(pattern)、替换内容(repl)、要处理的字符串(string)以及可选的计数参数(count,默认为 0)和标志参数(flags,默认为 0)。通过调用 subn 函数,可以在字符串中找到匹配指定模式的部分,并将其替换为指定的内容。函数返回一个包含替换后的字符串和实际替换的次数的元组。
使用 re.subn 函数,将字符串 '123456abcdef' 中所有的 '[1-2]' 替换为 'A',返回替换后的字符串以及替换的次数。具体为:先找到字符串中所有符合 '[1-2]' 模式的子串,然后将这些子串替换为 'A',最后返回替换后的字符串和替换的次数。
使用 re.sub 函数将字符串中的 "g.t" 替换为 "have",对字符串 'I get A, I got B,I gut C' 进行操作。首先找到 "g.t" 这个模式,然后将其替换为 "have",最后返回替换后的字符串。
使用 re.subn 函数,将字符串中的 "g.t" 替换为 "have"。对字符串 'I get A, I got B,I gut C' 进行替换操作,返回替换后的字符串以及替换的次数。
执行结果如下:
('AA3456abcdef', 2)
I have A, I have B ,I have C
我拥有 A,我拥有 B,我拥有 C。
四、一些注意点
re.findall 的区别:它找到所有匹配的子串,并以列表形式返回。
re.match 只会去匹配字符串的起始部分。倘若字符串的起始部分与正则表达式不相符,那么匹配就会失败,此时函数会返回 None。re.search 则是会对整个字符串进行匹配,一直到找到一个匹配为止。
在字符串"abc33"中搜索包含数字的内容,通过 re.search 函数进行查找,找到后使用 group 方法获取匹配到的内容,最终将匹配到的内容赋值给变量 a,即 a 的值为"3"。 print(a) 使用 re.match 函数,传入模式 '[\d]' 和字符串 "abc33",将匹配结果赋值给变量 p 。 print(p) 使用 re.findall 函数在字符串 "abc33" 中查找所有的数字字符。通过正则表达式 '[\d]' 进行匹配,该正则表达式表示匹配任意一个数字。最终返回匹配到的数字字符列表,这里返回的是 ['3', '3']。 print(b) 执行结果: 3 None ['3', '3']
2、贪婪匹配与非贪婪匹配
前面的*、+、?等都属于贪婪匹配,意思是会尽可能地进行匹配。而后面加上?号后,就会使其变成惰性匹配。
使用 re.findall 函数在字符串 'a23b' 中查找所有匹配模式 "a(\d+?)" 的内容,将匹配到的结果存储在变量 a 中。 print(a) b 是通过在字符串 'a23b' 中查找所有匹配模式 "a(\d+)" 的内容得到的。即从 'a23b' 中找出所有以 'a' 开头,后面跟着一个或多个数字的部分。 print(b) 执行结果: ['2'] ['23']
a = re.match('<(.*)>','title').group()
print(a)
b = re.match('<(.*?)>','title').group()
print(b)
执行结果:
title
使用 re 模块的 findall 函数来查找字符串 'a3333b' 中匹配模式 'a(\d+)b' 的内容,将匹配到的结果存储在变量 a 中。 print(a) 使用正则表达式 re 来查找字符串 'a3333b' 中符合模式 'a(\d+?)b' 的内容,将找到的结果赋值给变量 b 。 print(b) 执行结果如下: ['3333'] ['3333'] ####################### 这里需要注意的是,当前后均有限定条件时,就不存在贪婪模式了,并且非匹配模式会失效。
3、用flags时遇到的小坑
使用 re.split('a', '1A1a2A3', re.I) 进行分割,输出的结果未能区分大小写。
因为 re.split(pattern,string,maxsplit,flags) 默认有四个参数。当我们只传入三个参数时,系统会将 re.I 视为第三个参数,所以它没起作用。若要让这里的 re.I 起作用,写成 flags=re.I 就可以了。
五、正则的小实践
1、匹配电话号码
p 是通过 re.compile 函数编译的一个正则表达式对象,该正则表达式为 '\d{3}-\d{6}' ,即匹配三位数字后跟一个连字符再跟六位数字的模式。
使用 p 去查找所有匹配的内容,查找的内容是 '010-628888' ,然后将查找到的结果打印出来。
2、匹配IP
re.search 匹配的内容是一个由四个部分组成的字符串,每个部分都是一个点分隔的十进制数,范围在 0 到 255 之间。具体来说,第一个部分可以是 0 到 255 之间的任意一个数,第二个部分也可以是 0 到 255 之间的任意一个数,第三个部分同样可以是 0 到 255 之间的任意一个数,第四个部分也可以是 0 到 255 之间的任意一个数。例如,192.168.1.1 就符合这个模式。re.search 函数在给定的字符串 "192.168.1.1" 中查找是否存在这样的模式。