正则表达式

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+字,为了大家能理解,写的很详细,这也让我花了很多时间。最后,如果觉得本文对大家有帮助的话,还请给我点个赞和关注,谢谢大家!!!

相关推荐
databook9 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar10 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户83562907805110 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_10 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机17 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机18 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机18 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机18 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i18 小时前
drf初步梳理
python·django
每日AI新事件18 小时前
python的异步函数
python