正则表达式
正则表达式英文称regular expression
定义:正则表达式是一种文本模式匹配的工具,用于字符串的搜索,匹配和替换。在excel,word以及其他的文本编辑器都可直接适配。
一、基本匹配规则
-
字面值字符:例如字母、数字、空格等,可以直接匹配它们自身。
-
特殊字符:例如点号
.
、星号*
、加号+
、问号?
等,它们具有特殊的含义和功能。 -
字符类:用方括号
[ ]
包围的字符集合,用于匹配方括号内的任意一个字符。 -
元字符:例如
\d
、\w
、\s
等,用于匹配特定类型的字符,如数字、字母、空白字符等。 -
量词:例如
{n}
、{n,}
、{n,m}
等,用于指定匹配的次数或范围。 -
边界符号:例如
^
、$
、\b
、\B
等,用于匹配字符串的开头、结尾或单词边界位置。
1.字符匹配
普通字符匹配:直接匹配文本中的字符,如abc
表示直接匹配文本中包含为"abc"的字符
元字符匹配:元字符具有特殊的含义,例如 \d
匹配任意数字字符,\w
匹配任意字母数字字符,.
匹配任意字符(除了换行符)等。
常见的元字符如下:
元字符 | 含义 | 示例 |
---|---|---|
. |
匹配任意单个字符(换行符除外) | a.c 匹配 "abc"、"a1c" |
^ |
匹配字符串开头 | ^abc 匹配 "abc123" |
$ |
匹配字符串结尾 | abc$ 匹配 "123abc" |
* |
匹配前一个字符 0 次或多次 | a* 匹配 "", "a", "aaa" |
+ |
匹配前一个字符 1 次或多次 | a+ 匹配 "a", "aaa" |
? |
匹配前一个字符 0 次或 1 次 | a? 匹配 "", "a" |
{n} |
匹配前一个字符 n 次 | a{3} 匹配 "aaa" |
{n,m} |
匹配前一个字符 n 到 m 次 | a{2,4} 匹配 "aa", "aaa", "aaaa" |
[] |
匹配字符集合中的任意一个字符 | [abc] 匹配 "a", "b", "c" [a-z] 匹配小写字母 a 到 z 的任意一个 [0-9] 匹配数字 0 到 9 的任意一个 [^...] 匹配不在括号内的任意字符(字符集的否定) |
` | ` | 或运算 |
\ |
转义字符,转义元字符或定义特殊字符 | \d 、\s 、\w , C:\\Users\\Qiu |
\d |
匹配任何数字,相当于 [0-9] |
|
\D |
匹配任何非数字字符,相当于 [^0-9] |
|
\w |
匹配任何单词字符(字母、数字、下划线),相当于 [a-zA-Z0-9_] |
|
\W |
匹配任何非单词字符,相当于 [^a-zA-Z0-9_] |
|
\s |
匹配任何空白字符(空格、制表符、换行符等) | |
\S |
匹配任何非空白字符 | |
() |
用于分组和捕获子表达式。 | (\d{4})-(\d{2}-\d{2}) 将\d{4} 和\d{2}-\d{2} 分为两组,可以对这两组进行捕获以及后期直接引用 (\w{3})(\1) 表示匹配连续两段的字母,如abbabb |
(?: ) |
用于分组但不捕获子表达式。 |
2.量词匹配
-
*
:匹配前面的模式零次或多次。 -
+
:匹配前面的模式一次或多次。 -
?
:匹配前面的模式零次或一次。 -
{n}
:匹配前面的模式恰好 n 次。 -
{n,}
:匹配前面的模式至少 n 次。 -
{n,m}
:匹配前面的模式至少 n 次且不超过 m 次。
3.字符类
-
[ ]
:匹配括号内的任意一个字符。例如,[abc]
匹配字符 "a"、"b" 或 "c"。 -
[^ ]
:匹配除了括号内的字符以外的任意一个字符。例如,[^abc]
匹配除了字符 "a"、"b" 或 "c" 以外的任意字符。
4.边界匹配(定位符)
定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。
-
^
:匹配字符串的开头。 -
$
:匹配字符串的结尾。 -
\b
:匹配单词边界。 -
\B
:匹配非单词边界。
5.分组和捕获
-
( )
:用于分组和捕获子表达式。 -
(?: )
:用于分组但不捕获子表达式。
6.特殊字符
-
\
:转义字符,用于匹配特殊字符本身。 -
.
:匹配任意字符(除了换行符)。 -
|
:用于指定多个模式的选择。
7.分组与引用
()
表示分组,匹配子表达式并捕获;()
会把每个分组里的匹配的值保存起来, 多个匹配值可以通过数字 n 来查看(n 是一个数字,表示第 n 个捕获组的内容)。如:(\w{3})(\1)
表示匹配连续两段的字母,如abbabb
(\d{4})-(\d{2}-\d{2})
将\d{4}
和\d{2}-\d{2}
分为两组,可以对这两组进行捕获以及后期直接引用
但用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?:
放在第一个选项前来消除这种副作用。即?:
仅于分组但不捕获子表达式。
其中?:
为非捕获元之一,非捕获元表示不会将表达式内容包含在结果当中。同等的非捕获元还有?=
和 ?!
8.正、负向零宽断言
?=
:正向零宽断言 (Positive Lookahead)
-
exp1(?=exp2)
查找在exp2
前面的exp1
,且exp2
不会包含在结果当中 -
(?<=exp2)exp1
表示查找在exp2
前面的exp1
,且exp2
不会包含在结果当中
python
import re
pattern = r"I love (?=python)" # 匹配 "python" 前面带有 "I love " 的部分
text = "I love python, I love coding."
match = re.search(pattern, text)
if match:
print(match.group()) # 输出:I love
?!
:负向零宽断言 (Negative Lookahead)
-
exp1(?!exp2)
查找在exp2
后面不跟 的exp1
,且exp2
不会包含在结果当中。 -
(?<!exp2)exp1
查找在exp2
前面不跟 的exp1
,且exp2
不会包含在结果当中。
python
import re
pattern = r"I love (?!python)"
text = "I love coding, I love python." # 匹配不以 "python" 结尾的 "I love "
matches = re.findall(pattern, text)
print(matches) # 输出:['I love ']
?=
和 ?!
的区别:
特性 | ?= (正向断言) |
?! (负向断言) |
---|---|---|
断言结果 | 后面 必须是 pattern |
后面 不能是 pattern |
是否消耗字符 | 否 | 否 |
适用场景 | 验证后续内容符合预期 | 排除后续内容不符合预期 |
9.贪婪与非贪婪匹配
*
和 +
限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配。
**贪婪匹配:**尽可能多地匹配。
- 示例:
a.*b
匹配 "a123b456b" 中的 "a123b456b"。
**非贪婪匹配:**尽可能少地匹配。
-
语法:在元字符后加
?
。 -
示例:
a.*?b
匹配 "a123b456b" 中的 "a123b"。
案例:如<.*>
会匹配到如下html标签内的所有值
但是如果我们只需要匹配一个<h1>
标签,我们可以使用非贪婪匹配:<.*?>
通过在 * 、+ 或 ? 限定符之后放置 ?,该表达式从"贪婪"表达式转换为"非贪婪"表达式或者最小匹配。
10.标记/修饰符(flag)
符号 | 作用 | 示例 |
---|---|---|
i | ignore - 不区分大小写 | 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。 |
g | global - 全局匹配 | 查找所有的匹配项。 |
m | multi line - 多行匹配 | 使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。 |
s | 特殊字符圆点 . 中包含换行符 \n | 默认情况下的圆点 . 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。 |
- i表示不区分大小写:
- m表示多行匹配(每行都进行匹配)
- 默认情况下的圆点 . 是 匹配除换行符 \n 之外的任何字符,加上 s 之后, . 中包含换行符 \n。
python re库内的正则表达式支持的修饰符(可选标志)
re.IGNORECASE
(re.I
):忽略大小写。
re.MULTILINE
(re.M
):多行模式,^
和 $
会匹配每一行的开头和结尾。
re.DOTALL
(re.S
):单行模式,使 .
匹配换行符。
re.VERBOSE
(re.X
):允许在正则表达式中添加注释和多行书写。
其他标志如 re.ASCII
、re.LOCALE
等。
二、Python正则表达式API使用
Python 提供了 re
模块用于操作正则表达式。
函数 | 含义 |
---|---|
re.match |
从字符串开头匹配,返回 Match 对象 |
re.search |
在字符串中搜索第一个匹配 |
re.findall |
返回所有匹配结果的列表 |
re.finditer |
返回所有匹配结果的迭代器 |
re.sub |
替换匹配到的内容 |
re.split |
按正则表达式分割字符串 |
1.查找
方法 | 作用描述 |
---|---|
re.match |
从字符串的开头尝试匹配正则表达式,只返回第一个匹配的对象。 |
re.search |
搜索整个字符串,返回第一个匹配的对象。 |
re.findall |
搜索所有符合正则表达式的内容,返回匹配结果的列表。 |
re.finditer |
搜索所有符合正则表达式的内容,返回一个迭代器(每项是 Match 对象)。 |
1.re.match
/re.search
re.match
只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None,而 re.search
匹配整个字符串,直到找到一个匹配。
re.match(pattern, string, flags=0) # pattern正则表达式;string需要匹配的字符;flag修饰符模式。match匹配字符串的开始,如果字符串开始不符合正则表达式,则返回None,否则返回一个Match对象
re.search(pattern, string, flags=0) # re.search 匹配整个字符串,匹配成功返回一个Match对象,否则返回None
Match对象方法:
-
group(num=0)
当查找字符串分组时,group(num) 将返回对应num组的元组(1开始分组),group(0)/group(num)表示全部匹配字符串。 -
groups()
返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。
python
import re
line = "Cats are smarter than dogs"
# 匹配字符串的开始,如果开始不成功,则匹配失败
matchObj = re.match(r"dogs", line, re.M | re.I) # return:None
if matchObj:
print(matchObj.group())
else:
print("No match!!") # No match!!
# 匹配整个字符串直到找到至少一个匹配
matchObj = re.search(r"dogs", line, re.M | re.I) # return:dogs
if matchObj:
print(matchObj.group()) # dogs
else:
print("No match!!")
分组
text2 = "18812345678,他还有一个电话号码是18887654321,他爱好的数字是01234567891,他的座机是:0571-52152166,0571-52152177"
m1 = re.search(
r"(\d{4})-(\d{8})", text2
) # 返回一个Match对象,调用Match对象的group方法获取返回值
print(m1.group()) # 0571-52152166
print(m1.group(1)) # 0571,取表达式的第一组匹配值(\d{4})
print(m1.group(2)) # 52152166,取表达式的第二组匹配值(\d{8})
2.findall
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。
re.findall(pattern, string, flags=0)
或
pattern.findall(string[, pos[, endpos]])
-
pattern 匹配模式。
-
string 待匹配的字符串。
-
pos 可选参数,指定字符串的起始位置,默认为 0。
-
endpos 可选参数,指定字符串的结束位置,默认为字符串的长度。
python
import re
result1 = re.findall(r'\d+','runoob 123 google 456')
pattern = re.compile(r'\d+') # 查找数字
result2 = pattern.findall('runoob 123 google 456')
result3 = pattern.findall('run88oob123google456', 0, 10)
print(result1) # ['123', '456']
print(result2) # ['123', '456']
print(result3) # ['88', '12']
多个匹配模式,返回元组列表
import re
result = re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
print(result) # [('width', '20'), ('height', '10')]
3.re.finditer
和 findall
类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
python
re.finditer(pattern, string, flags=0)
import re
it = re.finditer(r"\d+","12a32bc43jf3")
for match in it:
print(match.group())
12
32
43
3
findall
和finditer
的区别
-
findall
返回的可能是元组列表,也可能是字符串列表,取决于正则表达式内是否分组 -
finditer
返回的是Match对象
的迭代器
2.替换
替换功能可以用正则表达式匹配到的部分内容进行替换,主要通过 re.sub
和 re.subn
方法实现。
方法 | 作用描述 |
---|---|
re.sub |
替换匹配到的内容,返回替换后的新字符串。 |
re.subn |
替换匹配到的内容,返回元组 (新字符串, 替换次数) 。 |
-
sub
:返回替换后的字符串。 -
subn
:返回包含替换后字符串和替换次数的元组。
1.re.sub&re.subn
re.sub(pattern, repl, string, count=0, flags=0) # 前三个为必选参数,后两个为可选参数。
-
pattern : 正则中的模式字符串。
-
repl : 替换的字符串,也可为一个函数。
-
string : 要被查找替换的原始字符串。
-
count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
-
flags : 编译时用的匹配模式,数字形式。
python
import re
phone = "2004-959-559 # 这是一个电话号码"
# 删除注释
num = re.sub(r'#.*$', "", phone)
print ("电话号码 : ", num)
# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print ("电话号码 : ", num)
# 当repl 参数是一个函数
import re
# 将匹配的数字乘以 2
def double(matched):
value = int(matched.group('value'))
return str(value * 2)
s = 'A23G4HFD567'
print(re.sub('(?P<value>\d+)', double, s)) # A46G8HFD1134
subn和sub用法一样,只是subn会额外将替换的次数一起封装成元组返回
python
text = "abc,adfasda,Abc,ABC"
sub = re.sub(r"(abc)", "***", text, flags=re.I)
subn = re.subn(r"(abc)", "***", text, flags=re.I)
print(sub) # re.sub返回替换后的字符串,***,adfasda,***,***
print(subn) # re.subn返回替换后的字符串和替换次数的元组,('***,adfasda,***,***', 3)
3.分割split
re.split(pattern, string[, maxsplit=0, flags=0])
-
pattern
正则表达式 -
string
需要匹配的字符串 -
maxsplit
分割次数,maxsplit=1 分割一次,默认为 0表示不限制次数。 -
flags
标志位
python
text6 = "abc, ddd? xxx. fff! asd"
split = re.split(
r"\s*[,?!.]\s*", text6
) # 将【,?!.】符号以及旁边的空格作为分隔符分割字符串,返回一个字符串数组
print(split) # ['abc', 'ddd', 'xxx', 'fff', 'asd']
re.split('a*', 'hello world') # 对于一个找不到匹配的字符串而言,split 不会对其作出分割
# ['hello world']
4.compile
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。语法格式为:
re.compile(pattern[, flags])
-
pattern : 一个字符串形式的正则表达式
-
flags 可选,表示匹配模式,比如忽略大小写,多行模式等
-
返回
RegexObject
对象,该对象可以调用匹配方法(如match
、search
、findall
等)。
python
# 编译正则表达式
pattern = re.compile(r"\d+")
# compile
# 使用编译的正则对象进行匹配和查找
result1 = pattern.findall("There are 3 cats and 4 dogs.")
result2 = pattern.findall("There are 6 cats and 8 dogs.")
print(result1) # 输出 ['3', '4']
print(result2) # 输出 ['6', '8']
re.compile()
是处理复杂正则表达式的最佳选择,尤其是需要频繁使用相同的正则时。它能提高代码效率、增强可读性,是 Python 正则表达式模块中非常重要的功能。