Python中正则表达式的应用非常广泛,如数据挖掘、数据分析、网络爬虫、输入有效性验证等。
正则表达式(Regular Expression,在代码中简写为regex、regexp、RE或re)是预先定义好的一个"规则字符串",通过这个"规则字符串"可以匹配、查找和替换那些符合"规则"的文本。可以把它当成增强版的通配符。
虽然文本的查找和替换功能可通过字符串提供的方法实现,但是实现起来极为困难,且运算效率比较低。而使用正则表达式实现这些功能会比较简单,且效率很高,唯一困难之处在于编写合适的正则表达式。Python提供了利用正则表达式实现文本的匹配、查找和替换等操作的 re 模块。
正则表达式中的字符
正则表达式是一种字符串,是由普通字符和元字符(Metacharacters)组成的。
(1)普通字符
普通字符是按照字符字面意义表示的字符,如CSDN - 专业开发者社区、http://www.baidu.com
(2)元字符
元字符是预先定义好的一些特殊字符串,如:\w 和 \.
元字符
元字符是用来描述其他字符的特殊字符,它由基本元字符和普通字符构成。基本元字符是构成元字符的组成要素。学习正则表达式某种意义上就是在学习元字符的使用。
字符 | 说明 |
---|---|
\ | 转义符,表示转义 |
. | 表示任意一个字符(但不包含换行符) |
+ | 表示重复一次或多次 |
* | 表示重复零次或多次 |
? | 表示重复零次或一次 |
| | 选择符号,表示"或关系",如:A | B 表示匹配A或B |
{} | 定义量词 |
() | 定义分组 |
^ | 可以表示取反或匹配一行的开始 |
$ | 匹配一行的结束 |
字符转义
反斜杠 " \ " 不仅可以对普通字符进行转义,还可以对基本元字符进行转义。如" . "表示任意一个字符,而" \. "表示普通字符点。
开始与结束字符
基本元字符 ^ 和 ,它们可以用于匹配一行字符串的开始和结束。当正则表达式以"\^"开始时,则从字符串的开始位置匹配;当正则表达式以""结束时,则从字符串的结束位置匹配。
注意:由于正则表达式中经常会有反斜杠" \ "等特殊字符,如果采用普通字符串表示需要转义。而在字符串前面加上 r 则不需要转义,因此一般正则表达式采用原始字符串表示。
示例代码如下
import re
p1 = r'\w+@zhangsan\.com' # 匹配包含 @zhangsan.com 的任何字符串中的任何位置
p2 = r'^\w+@zhangsan\.com$' # 要求整个字符串必须是有效的电子邮件地址,并且只匹配完整的字符串
text = "Tony's email is tony_guan588@zhangsan.com"
m = re.search(p1,text)
#re.search 函数在 text 中搜索与 p1 匹配的模式
# 如果找到匹配项,它将返回一个匹配对象;如果没有找到
# 它将返回 None。然后打印这个匹配对象
print(m)
m1 = re.search(p2,text)
print(m)
email1 = 'tony_guan588@zhangsan.com'
m2 = re.search(p2,email1)
print(m2)
代码运行结果
<re.Match object; span=(16, 41), match='tony_guan588@zhangsan.com'>
<re.Match object; span=(16, 41), match='tony_guan588@zhangsan.com'>
<re.Match object; span=(0, 25), match='tony_guan588@zhangsan.com'>
字符类
正则表达式可以使用字符类(Character class),一个字符类定义一组字符的集合,其中的任一字符出现在输入字符串中即匹配成功。注意每次匹配只能匹配字符类中的一个字符。
定义字符类
定义一个普通的字符类需要使用"["和"]"元字符类。例如想要在输入字符串中匹配Python或python,可以使用正则表达式[Pp]ython。
示例代码如下
import re
p = r'[Pp]ython'
m = re.search(p,'I like python')
print(m)
m1 = re.search(p,'I like Python')
print(m1)
m2 = re.search(p,'I like Java')
print(m2)
代码运行结果
<re.Match object; span=(7, 13), match='python'>
<re.Match object; span=(7, 13), match='Python'>
None
字符类取反
在正则表达式中指定不想出现的字符,可以在字符前加"^"符号
示例代码如下
import re
p = r'[^1234567890]' # 表示输入字符串中非0123456789即匹配
m = re.search(p,'10000')
print(m)
m1 = re.search(p,'I like Python')
print(m1)
代码运行结果
None
<re.Match object; span=(0, 1), match='I'>
区间
区间使用连字符"-"表示的,例如[0-9]等同于[0123456789];也可以表示多个不用区间,如[0-9a-zA-Z]表示所有的数字和字母字符类。
示例代码如下
import re
p = r'[0-9a-zA-Z]' # 表示输入字符串中非0123456789即匹配
m = re.search(p,'_10000')
print(m)
m1 = re.search(p,'I like Python')
print(m1)
代码运行结果
<re.Match object; span=(1, 2), match='1'>
<re.Match object; span=(0, 1), match='I'>
预定义字符类
字符 | 说明 |
---|---|
. | 匹配任意一个字符 |
\\ | 匹配反斜杠 \ 字符 |
\n | 匹配换行 |
\r | 匹配回车 |
\f | 匹配一个换页符 |
\t | 匹配一个水平制表符 |
\v | 匹配一个垂直制表符 |
\s | 匹配一个空格符,等价于[\t\n\r\f\v] |
\S | 匹配一个非空格符,等价于[^\s] |
\d | 匹配一个数字字符,等价于[0-9] |
\D | 匹配一个非数字字符,等价于[^0-9] |
\w | 匹配任何语言的单词字符、数字和下划线"_"等字符 |
\W | 等价于[^\w] |
量词
前面提到的正则表达式元字符只能匹配显示一次字符或字符串,如果想匹配显示多次字符或字符串可以使用量词,量词表示字符或字符串重复的次数。
使用量词
字符 | 说明 |
---|---|
? | 出现零次或一次 |
* | 出现零次或多次 |
+ | 出现一次或多次 |
{n} | 出现 n 次 |
{n,m} | 至少出现 n 次但不超过 m 次 |
{n,} | 至少出现 n 次 |
示例代码如下
import re
m = re.search(r'\d?','654321') # 出现数字一次
print(m)
m = re.search(r'\d?','ABC') # 出现数字0次
print(m)
m = re.search(r'\d*','654321') # 出现数字多次
print(m)
m = re.search(r'\d+','654321') # 出现数字多次
print(m)
m = re.search(r'\d+','ABC') # 不匹配
print(m)
m = re.search(r'\d{8}','87654321') # 出现数字8次
print(m)
m = re.search(r'\d{8}','ABC') # 不匹配
print(m)
m = re.search(r'\d{9,}','87654321') # 不匹配
print(m)
代码运行结果
<re.Match object; span=(0, 1), match='6'>
<re.Match object; span=(0, 0), match=''>
<re.Match object; span=(0, 6), match='654321'>
<re.Match object; span=(0, 6), match='654321'>
None
<re.Match object; span=(0, 8), match='87654321'>
None
None
Process finished with exit code 0
贪婪量词和懒惰量词
贪婪量词会尽可能多地匹配字符,懒惰量词会尽可能少地匹配字符。Python 中的正则表达式量词默认是贪婪的,要想使用懒惰量词在后面加"?"即可。
示例代码如下
import re
# 贪婪量词,会匹配数字 8 次
m = re.search(r'\d{5,8}','87654321') # 出现数字8次
print(m)
# 懒惰量词,会匹配数字 5 次
m = re.search(r'\d{5,8}?','87654321') # 出现数字8次
print(m)
代码运行结果
<re.Match object; span=(0, 8), match='87654321'>
<re.Match object; span=(0, 5), match='87654'>
分组
如果想让一个字符串作为整体使用量词,则需要对整体字符串进行分组,也称子表达式。
定义分组
定义正则表达式分组,则需要将字符串放到一对小括号中。
示例代码如下
import re
p = r'(121){2}'
m = re.search(p,'121121abcabc')
print(m)
print(m.group()) # 返回匹配字符串
print(m.group(1)) # 获得第一组内容
p = r'(\d{3,4}-\d{7,8})'
m = re.search(p, '010-87654321')
print(m)
print(m.group()) # 返回匹配字符串
print(m.groups()) # 获得所有组内容
代码运行结果
<re.Match object; span=(0, 6), match='121121'>
121121
121
<re.Match object; span=(0, 12), match='010-87654321'>
010-87654321
('010-87654321',)
Process finished with exit code 0
其中上面代码调用 match 对象的 group() 方法返回匹配的字符串,group() 方法语法如下:
match.group([1,...])
其中 1 代表组编号,在正则表达式中组编号是从 1 开始的
命名分组
在 Python 中访问分组时,除了可以通过组编号进行访问,还可以通过组名进行访问,前提是要在正则表达式中为组命名,组命名通过在组开头添加"?P<组名>"实现。当在程序中访问这些带有名字的组时,可以通过组编号或组名字进行访问。
反向引用分组
除了可以在程序代码中访问正则表达式匹配之后的分组内容,还可以在正则表达式内部访问之前的分组,称为"反向引用分组"。正则表达式中反向引用语法是"\组编号",组编号是从1开始的。如果组有名字,也可以用组名反向引用,语法是"(?P=组名)"。
示例代码如下
import re
p = r'<([\w]+)>.*</([\w]+)>' # 用于匹配 HTML 或 XML 等标记语言中的标签结构,并捕获标签的名称。
m = re.search(p,'<a>abc</a>')
print(m)
m = re.search(p,'<a>abc</b>')
print(m)
# 使用组编号反向引用
print()
p = r'<([\w]+)>.*</\1>'
'''
使用组名来反向引用
p = r"<(?P<tag>[\w]+)>.*</(?P=tag)>"
'''
m = re.search(p,'<a>abc</a>')
print(m)
m = re.search(p,'<a>abc</b>')
print(m)
代码运行结果
<re.Match object; span=(0, 10), match='<a>abc</a>'>
None
<re.Match object; span=(0, 10), match='<a>abc</a>'>
None
Process finished with exit code 0
非捕获分组
前文介绍的分组称为"捕获分组"。捕获分组的匹配结果被暂时保存到内存中,以备正则表达式或其他程序引用。这个过程称为"捕获",捕获结果可以通过组编号或组名进行引用。能够反向引用分组就是因为分组时捕获的。如果正则表达式比较复杂,要处理的文本有很多,可能严重影响性能。因此当使用分组,又不需要引用分组时,可使用"非捕获分组",在组开头使用"?:" 可以实现非捕获分组。需要注意非捕获分组不会匹配组内容。
示例代码如下
import re
p = r'(?:121){2}'
m = re.search(p,'121121abcabc')
print(m)
print(m.group())
print()
p = r'(?:\d{3,4}-(?:\d{7,8}))'
m = re.search(p,'010-12345678')
print(m)
print(m.group())
print(m.groups())
代码运行结果
<re.Match object; span=(0, 6), match='121121'>
121121
<re.Match object; span=(0, 12), match='010-12345678'>
010-12345678
()
Process finished with exit code 0
re模块中重要函数
re是Python内置的正则表达式模块
search() 和 match() 函数
函数名 | 说明 |
---|---|
search(a,b) | 在输入的字符串中查找,返回第一个匹配的内容,如果找到则match 对象;如果没有找到则返回None;其中参数 a 为查找的规则(正则表达式),参数 b 为要查找的字符串 |
match(a,b) | 在输入的字符串开始处查找匹配内容,如果找到一个则match 对象;如果没有找到则返回None;其中参数 a 为查找的规则(正则表达式),参数 b 为要查找的字符串 |
函数名 | 说明 |
---|---|
group() | 返回匹配的字符串 |
start() | 返回子字符串的开始索引 |
end() | 返回子字符串的结束索引 |
span() | 返回子字符串的跨度 |
[match对象的常用方法] |
示例代码如下
import re
p = r'\w+@zhangsan\.com'
text = "Tony's email is tony_guan588@zhangsan.com"
m = re.search(p,text)
print(m) # successful
m1 = re.match(p,text)
print(m1) #fail
text1 = "tony_guan588@zhangsan.com"
m1 = re.match(p,text1)
print(m1) # successful
# match对象的几个方法:
print()
print(m1.group()) # 返回匹配的字符串
print(m1.start()) # 返回子字符串的开始索引
print(m1.end()) # 返回子字符串的结束索引
print(m1.span()) # 返回子字符串的跨度
代码运行结果
<re.Match object; span=(16, 41), match='tony_guan588@zhangsan.com'>
None
<re.Match object; span=(0, 25), match='tony_guan588@zhangsan.com'>
tony_guan588@zhangsan.com
0
25
(0, 25)
Process finished with exit code 0
findall() 和 finditer() 函数
示例代码如下
import re
p = r'[Pp]ython'
text = 'I like Python and python'
match_list = re.findall(p,text) # 返回match 列表对象
print(match_list)
match_iter = re.finditer(p,text) # 返回可迭代对象
for m in match_iter:
print(m.group())
代码运行结果
['Python', 'python']
Python
python
Process finished with exit code 0
字符串分割
字符串分割使用 split() 函数,该函数按照匹配的子字符串进行字符串分割,返回字符串列表对象,split()函数语法如下
re.split(pattern,string,maxsplit = 0,flags = 0)
其中参数 pattern 是正则表达式;参数 string 是要分割的子字符串;参数 maxsplit 是最大分割次数,默认值为0,表示分割次数没有限制;参数 flags 是编译标志。
示例代码如下
import re
p = r'\d+'
# 根据匹配的规则即匹配到数字就分割
text = 'AB12CD34EF'
clist = re.split(p,text)
print(clist)
text1 = 'AB12CD34EF56'
clist1 = re.split(p,text1)
print(clist1)
clist2 = re.split(p,text,maxsplit=1) # 设置最大分割次数为1
print(clist2)
clist3 = re.split(p,text,maxsplit=2) # 设置最大可能分割次数为2
print(clist3)
代码运行结果
['AB', 'CD', 'EF']
['AB', 'CD', 'EF', '']
['AB', 'CD34EF']
['AB', 'CD', 'EF']
Process finished with exit code 0
字符串替换
字符串替换使用 sub() 函数,该函数用于替换匹配的子字符串,返回值是替换之后的字符串,其语法如下
re.sub(pattern,rep,string,count=0,flags=0)
其中参数 pattern 是正则表达式;参数 rep 是替换字符串;参数 string 是要提供的字符串;参数 count 是要替换的最大数量,默认值为0,表示替换数量没有限制;参数 flags 是编译标志。
示例代码如下
import re
p = r'\d+'
text = 'AB12CD34EF56'
replace_text = re.sub(p,' ',text)
print(replace_text)
replace_text1 = re.sub(p,' ',text,count=1) # 只替换一次
print(replace_text1)
代码运行结果
AB CD EF
AB CD34EF56
Process finished with exit code 0
编译正则表达式
编译的正则表达式可以重复使用,这样能减少正则表达式的解析和验证,提高效率。在 re 模块中的 compile() 函数可以编译正则表达式,其语法如下:
re.compile(pattern [, flags=0])
其中参数 pattern 是正则表达式,参数 flags 是编译标志,compile() 函数返回一个编译的正则表达式对象 regex。
已编译正则表达式对象
compile() 函数返回一个编译的正则表达式对象,该对象也提供了与 re 模块类似的方法。下面表格中参数 pos 为开始查找索引,参数 endpos 为结束查找索引。
常用函数 | 已编译正则表达式对象方法 | re 模块函数 |
---|---|---|
search() | regex.search(string[,pos[,endpos]]) | re.search(pattern,string,flags=0) |
match() | regex.match(string[pos,[endpos]]) | re.match(pattern,string,flags=0) |
findall | regex.findall(string[pos,[endpos]]) | re.findall(pattern,string,flags=0) |
finditer() | regex.finditer(string[pos,[endpos]]) | re.finditer(pattern,string,flags=0) |
sub() | regex.sub(repl,string,count=0) | re.sub(pattern,repl,string,count=0,flags=0) |
split() | regex.split(string,maxsplit=0) | re.split(pattern,string,maxsplit=0,flags=0) |
[已编译正则表达式对象方法与 re 模块函数对照] |
示例代码如下
import re
# 通过编译的正则表达式对象 regex 调用方法实现文本匹配、查找和替换操作
p = r'\w+@zhangsan\.com'
regex = re.compile(p) # 编译正则表达式
text = "Tom's email is tom_guan588@zhangsan.com."
m = regex.search(text)
print(m)
p1 = r'[Pp]ython'
regex1 = re.compile(p1) # 编译正则表达式
text1 = 'I like Python and python'
match_list = regex1.findall(text1)
print(match_list)
match_iter = regex1.finditer(text1)
for m in match_iter:
print(m)
p = r'\d+'
regex = re.compile(p) # 编译正则表达式
text3 = 'AB12CD34EF56'
clist = regex.split(text3)
print(clist)
replace_text = regex.sub(' ',text3)
print(replace_text)
代码运行结果
<re.Match object; span=(15, 39), match='tom_guan588@zhangsan.com'>
['Python', 'python']
<re.Match object; span=(7, 13), match='Python'>
<re.Match object; span=(18, 24), match='python'>
['AB', 'CD', 'EF', '']
AB CD EF
Process finished with exit code 0
编译标志
compile() 函数编译正则表达式对象时,还可以设置编译标志。编译标志可以改变正则表达式引擎行为。
(1)ASCII 和 Unicode
通过编译标志 re.ASCII(或re.A)设置采用ASCII编码,通过编译标志 re.UNICODE(或re.U)设置采用 Unicode 编码。
示例代码如下
import re
text = '你们好 Hello'
p = r'\w+'
regex = re.compile(p,re.U) # 设置采用Unicode编码,此时所有字符都可以匹配
m = regex.search(text)
print(m)
m = regex.match(text)
print(m)
regex = re.compile(p,re.A) # 设置采用ASCII编码,此时中文并不会显示出来
m = regex.search(text)
print(m)
m = regex.match(text) # 匹配失败,因为开头为中文,而ASCII编码并没有中文
print(m)
代码运行结果
<re.Match object; span=(0, 3), match='你们好'>
<re.Match object; span=(0, 3), match='你们好'>
<re.Match object; span=(4, 9), match='Hello'>
None
Process finished with exit code 0
(2)忽略大小写
默认情况下正则表达式引擎对大小写是敏感的,可以通过 re.IGNORECASE(或re.I)实现忽略大小写。
示例代码如下
import re
p = r'(java).*(python)'
regex = re.compile(p,re.I)
m = regex.search('I like Java and Python.')
print(m)
m1 = regex.search('I like JAVA and Python.')
print(m1)
代码运行结果
<re.Match object; span=(7, 22), match='Java and Python'>
Process finished with exit code 0
(3)点元字符匹配换行符
默认情况下正则表达式引擎中点 "." 元字符可以匹配除换行符外的任何字符,但是有时候需要点 "." 元字符也能匹配换行符,此时可以通过 re.DOTALL(或re.S)实现。
示例代码如下
import re
p = r'.+'
regex = re.compile(p) # 没有设置编译标志
m = regex.search('Hello\nWorld.')
print(m)
regex1 =re.compile(p,re.DOTALL)
m1 = regex1.search('Hello\nWorld.')
print(m1)
代码运行结果
<re.Match object; span=(0, 5), match='Hello'>
<re.Match object; span=(0, 12), match='Hello\nWorld.'>
Process finished with exit code 0
(4)多行模式
编译标志 re.MULTILINE(或 re.M)可以设置多行模式,默认情况下 ^ 和 $ 匹配字符串的开始和结束,而在多行模式下 ^ 和 $ 匹配任意一行的开始和结束。
示例代码如下
import re
p = r'^World'
regex = re.compile(p) # 未设置多行模式
m = regex.search('Hello\nWorld.')
print(m)
regex1 = re.compile(p,re.M) # 设置多行模式
m1 = regex1.search('Hello\nWorld.')
print(m1)
代码运行结果
None
<re.Match object; span=(6, 11), match='World'>
Process finished with exit code 0
(5)详细模式
编译标志 re.VERBOSE(或 re.X)可以设置详细模式,详细模式下可以在正则表达式中添加注释,可以有空格和换行。
示例代码如下
import re
p = '''(java) # 匹配java字符串
.* # 匹配任意字符零或多个
(python) # 匹配Python字符串
'''
# 由于正则表达式中包含了换行等符号,所以需要使用双重单引号或三重单引号括起来,而不是原始字符串
regex = re.compile(p,re.I | re.VERBOSE) # 设置了两个编译标志
m = regex.search('I like Java and Python.')
print(m)
m1 = regex.search('I like JAVA and Python.')
print(m1)
m2 = regex.search('I like java and Python.')
print(m2)
代码运行结果
<re.Match object; span=(7, 22), match='Java and Python'>
<re.Match object; span=(7, 22), match='JAVA and Python'>
<re.Match object; span=(7, 22), match='java and Python'>
Process finished with exit code 0
参考书籍:《python从小白到大牛》(第2版)关东升 编著
文章创作不易,本文共11000+字,为了大家能理解,写的很详细,这也让我花了很多时间。最后,如果觉得本文对大家有帮助的话,还请给我点个赞和关注,谢谢大家!!!