一、认识正则表达式
正则表达式(Regular Expression,简称 "正则")是一种用于描述字符模式的工具,本质是 "用一套规则定义你想匹配的字符串格式"。
掌握正则的核心是理解 "如何用特殊符号组合出你需要的模式"
一、正则的核心作用与学习前提
- 核心作用:从文本中快速匹配、查找、替换符合特定格式的字符串
- 学习前提:正则的 "规则" 由 "普通字符" 和 "元字符"(有特殊含义的符号)组成,学习的关键是理解每个元字符的作用,再通过组合解决复杂问题。
二、基础:从 "匹配固定文本" 开始
最简单的正则是直接写你要匹配的文本(普通字符),正则会精确匹配这段文本。
例 1:匹配固定字符串
- 正则:
hello
- 作用:在目标文本中找 "hello" 这个连续字符串。
转义字符:匹配 "有特殊含义的普通字符"
如果要匹配的文本中包含元字符 (如. * + ? ^ $ ( ) [ ] { } | \
),需要用\
(转义符)让它们失去特殊含义,变成普通字符。
例 2:匹配包含特殊字符的文本
- 需求:匹配 "a.b"(中间是点)。
- 错误正则:
a.b
(.
是元字符,会匹配任意字符,比如 "a1b""a@b" 都会被匹配)。 - 正确正则:
a\.b
(\
转义后,.
仅匹配字面意义的点)。
三、元字符:赋予正则 "描述模式" 的能力
1. 匹配 "任意字符":.
(点)
- 作用:匹配除换行符(\n)之外的任意一个字符(字母、数字、符号、空格等)。
- 例:
正则:a.b
匹配:"a1b""a@b""a b"(a 和 b 之间是任意字符),不匹配 "ab"(中间没有字符)、"a\nb"(中间是换行)。
2. 匹配 "字符出现次数":量词(*
+
?
{n}
{n,}
{n,m}
)
量词用于描述 "它前面的字符 / 子模式" 出现的次数,是正则简化规则的核心。
量词 | 作用 | 例(正则:a{量词}b ) |
匹配结果 |
---|---|---|---|
* |
前面的元素出现0 次或多次(可以没有,也可以很多) | a*b |
匹配 "b"(a 出现 0 次)、"ab"(1 次)、"aaab"(3 次) |
+ |
前面的元素出现1 次或多次(至少 1 次) | a+b |
匹配 "ab"(1 次)、"aaab"(3 次),不匹配 "b"(0 次) |
? |
前面的元素出现0 次或 1 次(最多 1 次) | a?b |
匹配 "b"(0 次)、"ab"(1 次),不匹配 "aab"(2 次) |
{n} |
前面的元素恰好出现 n 次 | a{2}b |
只匹配 "aab"(a 恰好 2 次) |
{n,} |
前面的元素出现至少 n 次 | a{2,}b |
匹配 "aab"(2 次)、"aaab"(3 次),不匹配 "ab"(1 次) |
{n,m} |
前面的元素出现n 到 m 次(包含 n 和 m) | a{1,3}b |
匹配 "ab"(1 次)、"aab"(2 次)、"aaab"(3 次),不匹配 "aaaaab"(5 次) |
3. 匹配 "特定范围的字符":字符集([ ]
)
用[]
包裹的字符表示 "匹配其中任意一个字符",支持范围简写(如[0-9]
表示 0 到 9 的数字)。
字符集 | 作用 | 等价写法 |
---|---|---|
[abc] |
匹配 a、b、c 中的任意一个 | 无 |
[0-9] |
匹配任意一个数字 | \d (元字符简写) |
[a-z] |
匹配任意一个小写字母 | 无 |
[A-Z] |
匹配任意一个大写字母 | 无 |
[a-zA-Z0-9_] |
匹配字母、数字、下划线(单词字符) | \w (元字符简写) |
[^abc] |
匹配除了 a、b、c 之外 的任意字符(^ 在[] 内表示 "非") |
无 |
[0-9a-fA-F] |
匹配十六进制字符(0-9、a-f、A-F) | 无 |
4. 元字符简写:常用字符集的快捷方式
为了简化正则,常用字符集有固定简写(都是反斜杠开头):
简写 | 作用 | 对应字符集 |
---|---|---|
\d |
匹配任意数字 | [0-9] |
\D |
匹配任意非数字 | [^0-9] |
\w |
匹配单词字符(字母、数字、下划线) | [a-zA-Z0-9_] |
\W |
匹配非单词字符 | [^a-zA-Z0-9_] |
\s |
匹配空白字符(空格、制表符\t 、换行符\n 等) |
[ \t\n\r\f\v] |
\S |
匹配非空白字符 | [^ \t\n\r\f\v] |
5. 匹配 "位置":边界符(^
$
\b
\B
)
边界符不匹配具体字符,只匹配 "位置"(比如字符串开头、结尾、单词边界),是精准匹配的关键。
边界符 | 作用 | 例 |
---|---|---|
^ |
匹配字符串的开头 (注意:在[] 内是 "非" 的意思,在外是 "开头") |
正则^hello :只匹配 "hello world"(hello 在开头),不匹配 "world hello"(hello 在中间) |
$ |
匹配字符串的结尾 | 正则world$ :只匹配 "hello world"(world 在结尾),不匹配 "world hello"(world 在开头) |
\b |
匹配单词边界 (单词字符\w 和非单词字符\W 之间的位置) |
正则\bcat\b :匹配 "cat""a cat"(cat 是独立单词),不匹配 "category"(cat 是单词的一部分) |
\B |
匹配非单词边界 (与\b 相反) |
正则\Bcat\B :匹配 "category"(cat 在单词中间),不匹配 "cat""a cat" |
6. 匹配 "多个模式中的一个":选择符(|
)
|
表示 "或",用于匹配多个模式中的任意一个,类似编程语言中的or
。
-
例:
正则:
cat|dog
作用:匹配 "cat" 或 "dog" 中的任意一个。
匹配结果:能匹配 "I have a cat" 中的 "cat"、"I have a dog" 中的 "dog",不匹配 "bird"。
-
注意:
|
的优先级很低,如需限定范围,需用()
包裹(见 "分组")。例:正则
I like (cat|dog)
匹配 "I like cat" 或 "I like dog",而I like cat|dog
会匹配 "I like cat" 或 "dog"(范围错误)。
四、进阶:分组、引用与断言
掌握基础后,通过 "分组""引用""断言" 可以处理更复杂的场景(如提取子内容、条件匹配等)。
1. 分组(()
):将多个字符视为一个整体
()
用于将多个字符 / 元字符组合成一个 "子模式",让量词、选择符等作用于整个组,而不是单个字符。
-
例 1:让量词作用于组
需求:匹配 "abab""ababab"("ab" 重复 2 次或 3 次)。
正则:
(ab){2,3}
解析:
(ab)
是一个组,{2,3}
表示这个组重复 2-3 次,匹配 "abab"(2 次)、"ababab"(3 次)。 -
例 2:让选择符作用于组
需求:匹配 "2023-10-05" 或 "2023/10/05"(日期,分隔符是 - 或 /)。
正则:
\d{4}(-|/)\d{2}\1\d{2}
解析:
(-|/)
是组 1(匹配 - 或 /),\1
引用组 1 的结果(保证分隔符一致,比如前面是 -,后面也必须是 -)。
2. 引用:复用分组匹配的内容
分组匹配的内容会被 "捕获",可以通过\n
(正则中)或group(n)
(代码中)引用,n
是分组编号(从 1 开始)。
- 例:验证 HTML 标签是否闭合(如
<div>...</div>
)
正则:<(\w+)>.*?</\1>
解析:(\w+)
是组 1(匹配标签名,如 div、p);.*?
匹配标签内的内容(非贪婪模式);</\1>
引用组 1 的结果(确保闭合标签与开头标签名一致,如</div>
)。
匹配结果:能匹配<div>test</div>
,不匹配<div>test</p>
(标签名不一致)。
3. 断言(零宽断言):"条件判断" 式匹配
断言用于 "判断某个位置的前后是否符合特定条件",但不匹配实际字符(零宽),类似 "先看条件,再决定是否匹配"。
常用断言:
断言 | 作用 | 例 |
---|---|---|
(?=pattern) |
正向先行断言:匹配 "后面符合 pattern" 的位置 | 正则\d+(?=元) :匹配 "100 元" 中的 "100"(后面是 "元"),不匹配 "100 美元" |
(?!pattern) |
负向先行断言:匹配 "后面不符合 pattern" 的位置 | 正则\d+(?!元) :匹配 "100 美元" 中的 "100"(后面不是 "元"),不匹配 "100 元" |
(?<=pattern) |
正向后行断言:匹配 "前面符合 pattern" 的位置 | 正则(?<=¥)\d+ :匹配 "¥100" 中的 "100"(前面是 "¥"),不匹配 "$100" |
(?<!pattern) |
负向后行断言:匹配 "前面不符合 pattern" 的位置 | 正则(?<!¥)\d+ :匹配 "$100" 中的 "100"(前面不是 "¥"),不匹配 "¥100" |
五、贪婪模式与非贪婪模式:控制匹配范围
量词默认是 "贪婪模式"------ 尽可能匹配更多字符;在量词后加?
会变成 "非贪婪模式"------ 尽可能匹配更少字符,这是解决 "匹配过量" 的关键。
- 例:从
<div>test</div><p>abc</p>
中提取第一个标签- 贪婪模式正则:
<.*>
(.*
尽可能多匹配)
匹配结果:<div>test</div><p>abc</p>
(整个字符串,过量)。 - 非贪婪模式正则:
<.*?>
(.*?
尽可能少匹配)
匹配结果:<div>
- 贪婪模式正则:
六、模式修饰符:调整正则的匹配规则
修饰符(flags)用于全局调整正则的行为(如是否忽略大小写、是否多行匹配等),常用修饰符:
修饰符 | 作用 | Python 中使用方式 |
---|---|---|
re.I (IGNORECASE ) |
忽略大小写匹配 | re.search(r'hello', text, re.I) 会匹配 "Hello""HELLO" 等 |
re.M (MULTILINE ) |
多行模式:让^ 匹配每行开头,$ 匹配每行结尾 |
文本有换行时,^hello 会匹配每行开头的 "hello" |
re.S (DOTALL ) |
让. 匹配换行符(默认不匹配) |
re.search(r'a.b', 'a\nb', re.S) 会匹配 "a\nb"(中间是换行) |
七、写正则的步骤:从需求到规则
- 明确需求:你要匹配什么?排除什么?(比如 "匹配 11 位手机号,必须以 13/15/17/18 开头")。
- 拆解规则:把需求拆成最小的字符 / 位置规则(比如手机号:第一位是 1,第二位是 3/5/7/8,后面 9 位是任意数字)。
- 用元字符翻译规则 :
- 第一位 1 →
1
; - 第二位 3/5/7/8 →
[3578]
; - 后面 9 位数字 →
\d{9}
; - 组合:
^1[3578]\d{9}$
(^
和$
确保是完整的 11 位,不包含其他字符)。
- 第一位 1 →
- 测试与调整:用实际文本测试,比如 "13812345678"(匹配)、"12345678901"(不匹配,第二位不是 3/5/7/8)、"1381234567"(不匹配,长度不够),根据结果调整正则。
八、常用正则示例(结合实际场景)
- 匹配手机号 :
^1[3-9]\d{9}$
(11 位,第一位 1,第二位 3-9,后 9 位数字)。 - 匹配邮箱 :
^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
(用户名 @域名。后缀)。 - 匹配 HTML 中的 value 属性 :
\bvalue\s*=\s*"([^"]+)"
(提取双引号内的属性值,([^"]+)
是组 1,用于提取内容)。 - 匹配日期(yyyy-mm-dd) :
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
(月份 1-12,日期 1-31)。
二、re的方法 - search
re.search('value=\"\\S+?\"', form_data_html)
re.search(pattern, string)
是 Python re
模块的核心函数之一,作用是:
在 string
(这里是 form_data_html
,通常是 HTML 文本)中,全局搜索第一个能匹配 pattern
(这里是正则表达式)的子串。
- 它和
re.match
的区别:re.match
只从字符串开头 匹配,而re.search
会检查字符串所有位置 ,只要找到第一个匹配就返回结果(没找到返回None
)。 - 这里的核心目标:在 HTML 文本中,找到包含
value="非空内容"
的片段(比如value="username"
、value="123456"
)。
首先要处理一个关键问题:转义字符 。
Python 字符串中,\
是 "转义符"------ 比如 \"
表示 "匹配一个双引号 "
"(因为双引号在字符串中是边界符号,直接写 "
会被认为是字符串结束);\\
表示 "匹配一个反斜杠 \
"(因为 \
本身需要转义)。
所以,'value=\"\\S+?\"'
这个 Python 字符串,在正则引擎中实际表示的正则是:
value="\S+?"
接下来,我们逐部分拆解这个 "实际正则"(value="\S+?"
):
1. 第一部分:value="
------ 固定前缀(字面匹配)
- 含义:精确匹配
value="
这 6 个字符,一个都不能错。 - 作用:锁定 "HTML 中
value
属性的赋值开头"。因为我们要找的是<input value="xxx">
这类标签中的value
属性,所以必须以value="
为起点,避免匹配到其他无关内容(valuexxx="xxx"
)。 - 例子:能匹配
value="abc"
、value="123"
的开头,但不能匹配Value="abc"
(大小写敏感,V
不等于v
)、value = "abc"
(多了空格)。
2. 第二部分:\S+?
------ 核心匹配(非空非空白内容)
这是整个正则的 "灵魂",负责匹配 value="
和 "
之间的 "非空内容",拆解为 \S
、+
、?
三个小部分:
符号 | 通俗含义 | 作用 | 例子 |
---|---|---|---|
\S |
非空白字符 | 匹配 "不是空格、换行符、制表符" 的任意一个字符(比如字母、数字、符号 !@# 等) |
能匹配 a 、5 、! ;不能匹配空格 、换行 \n |
+ |
一个或多个 | 表示 "前面的 \S 至少出现 1 次",确保 value 的值不是空的 |
能匹配 a (1 次)、abc (3 次);不能匹配空值(0 次) |
? |
非贪婪模式 | 表示 "尽可能少地匹配",避免把后面的 " 或其他内容也包含进来(关键!) |
比如遇到 value="a" b="c" ,只匹配 a (而不是 a" b="c ) |
- 通俗理解:
\S+?
就是 "至少 1 个非空白字符,且见好就收 ",确保只匹配value
引号内的核心内容,不 "贪多"。
3. 第三部分:"
------ 固定后缀(字面匹配)
- 含义:精确匹配一个双引号
"
。 - 作用:锁定 "HTML 中
value
属性的赋值结尾",确保匹配到完整的value="内容"
结构,而不是半截内容(比如只匹配value="abc
而漏掉末尾的"
)。 - 例子:配合前面的部分,能完整匹配
value="abc"
、value="123"
,但不能匹配value="abc
(缺少末尾"
)。
正则其实会匹配data-value
中的value
片段!
比如文本是 <input data-value="789">
,其中包含连续的字符片段 value="789"
(从data-value
的v
开始,到"
结束)。
用原正则 re.search('value=\"\\S+?\"', 'data-value="789"')
测试,结果是 能匹配到 ,匹配的片段就是 value="789"
------ 这说明正则确实 "从value
直接看起了",它不管前面的data-
,只要value="..."
的字符模式连续出现,就会匹配。
- 从第一个字符
d
开始:检查d
是否等于模式的第一个字符v
?不等,跳过; - 从第二个字符
a
开始:a
vsv
?不等,跳过; - 从第三个字符
t
开始:t
vsv
?不等,跳过; - 从第四个字符
a
开始:a
vsv
?不等,跳过; - 从第五个字符
-
开始:-
vsv
?不等,跳过; - 从第六个字符
v
开始:v
vsv
?相等!继续检查下一个字符; - 第七个字符
a
vs 模式的a
?相等; - 第八个字符
l
vs 模式的l
?相等; - 第九个字符
u
vs 模式的u
?相等; - 第十个字符
e
vs 模式的e
?相等; - 第十一个字符
=
vs 模式的=
?相等; - 接下来的
"789"
也符合模式的"\S+?"
,所以最终匹配成功,得到片段value="789"
。
简单说:正则不会 "主动找value
",但会 "逐个位置试",试到value
的位置时,发现模式匹配,就会返回结果 ------ 这就是它 "从data-value
的value
开始匹配" 的原因。
如何约束正则:只匹配独立的value
属性,不匹配data-value
中的片段?
核心是给value
加 "边界约束",确保value
前面不是 "字母、数字、下划线"(这些是构成属性名的字符),常用两种方式:
1. 用\b
(单词边界):确保value
是独立的 "单词"
\b
在正则中表示 "单词边界"------ 即value
前面的字符,不能是 "字母、数字、下划线"(这些是构成 HTML 属性名的字符)。
修改正则为:r'\bvalue=\"\\S+?\"'
- 匹配独立
value
属性:<input value="789">
中,value
前面是<
或空格(不是字母 / 数字 / 下划线),符合\b
,会匹配; - 不匹配
data-value
:<input data-value="789">
中,value
前面是-
和a
(a
是字母,属于单词字符),不符合\b
,不会匹配。
2. 用(?<!\w)
(负向零宽断言):更精准的边界约束
(?<!\w)
表示 "value
前面不能有任何单词字符(字母、数字、下划线)",效果和\b
类似,但更直观:
正则修改为:r'(?<!\w)value=\"\\S+?\"'
- 作用和
\b
一致,但更明确表达 "前面不能有单词字符" 的逻辑,避免\b
的其他歧义(比如value
后面的边界)。
⭐ 总结
- 正则会 从
data-value
中的value
开始匹配 ------ 只要value="..."
这个字符片段连续存在,不管前后是什么; - 正则之所以 "不分
value
和data-value
",是因为它没有 HTML 语义理解能力,只认字符模式; - 你真正需要的是 "给正则加边界约束"(如
\b
或(?<!\w)
),让它只匹配 "前面没有字母 / 符号" 的value
,也就是独立的value
属性,从而排除data-value
中的无关片段。
简单说:不是正则 "不能从value
看起",而是它 "看得太随意",需要我们给它 "划边界",让它只看 "对的value
"。
三、re的方法大全
一、匹配与查找类:定位或提取符合规则的内容
这类方法的核心是 "在字符串中找到符合正则的内容",但返回结果的形式和匹配范围不同。
1. re.match(pattern, string, flags=0)
------ 仅从字符串开头匹配
核心作用
只检查字符串的 最开头 是否符合正则规则,一旦开头不匹配,直接返回 None
(区别于 re.search
的 "全局查找")。
适合场景:验证字符串是否以特定内容开头(如验证手机号是否以 13/15/17/18
开头)。
语法与示例
pattern
:正则表达式;string
:要匹配的字符串;flags
:可选参数(如re.IGNORECASE
忽略大小写)。
TypeScript
import re
# 示例1:验证字符串是否以"Hello"开头(开头匹配成功)
result = re.match(r"Hello", "Hello World!")
print(result.group()) # 输出:Hello(用group()获取匹配到的内容)
# 示例2:验证手机号是否以13/15/17/18开头(正则:^1[3578]\d{9}$,但match默认从开头匹配,可省略^)
phone = "13812345678"
result = re.match(r"1[3578]\d{9}", phone)
print(result.group() if result else "手机号格式错误") # 输出:13812345678
# 示例3:开头不匹配(返回None)
result = re.match(r"Hello", "Hi Hello World!")
print(result) # 输出:None
注意事项
re.match
隐含 "开头匹配"(相当于正则加了^
),无需额外写^
;- 若需匹配整个字符串,需在正则末尾加
$
(如r"1[3578]\d{9}$"
),否则会匹配 "以目标开头但后续内容无关" 的字符串(如13812345678abc
也会匹配成功)。
2. re.findall(pattern, string, flags=0)
------ 提取所有匹配的子串
核心作用
遍历整个字符串,找到 所有 符合正则的子串,返回一个 列表 (无匹配时返回空列表)。
适合场景:批量提取内容(如提取文本中所有邮箱、所有手机号、所有数字)。
TypeScript
import re
# 示例1:提取文本中所有邮箱(正则:\w+@\w+\.\w+)
text = "我的邮箱是a@163.com,他的邮箱是b@qq.com,还有c@google.com"
emails = re.findall(r"\w+@\w+\.\w+", text)
print(emails) # 输出:['a@163.com', 'b@qq.com', 'c@google.com']
# 示例2:提取文本中所有数字(正则:\d+)
text = "订单1:金额100元,订单2:金额250元,订单3:金额50元"
numbers = re.findall(r"\d+", text)
print(numbers) # 输出:['1', '100', '2', '250', '3', '50']
# 示例3:无匹配(返回空列表)
empty_result = re.findall(r"\d+", "无数字文本")
print(empty_result) # 输出:[]
注意事项
-
若正则中有 分组 (用
()
包裹的部分),findall
会优先返回分组内的内容(而非整个匹配结果)。例如:TypeScript# 正则有分组,提取邮箱的"用户名"部分(@前面的内容) usernames = re.findall(r"(\w+)@\w+\.\w+", text) print(usernames) # 输出:['a', 'b', 'c']
-
若需返回整个匹配结果(而非分组),可将正则整体用
()
包裹(即r"(\w+@\w+\.\w+)"
)。
3. re.finditer(pattern, string, flags=0)
------ 迭代器形式返回所有匹配
核心作用
与 re.findall
类似,也是找到所有符合正则的子串,但返回的是 迭代器 (而非列表),每个迭代元素是 Match
对象(需用 group()
提取内容)。
适合场景:处理 大量匹配结果(如几万条数据),迭代器占用内存更少,效率更高。
TypeScript
import re
text = "我的邮箱是a@163.com,他的邮箱是b@qq.com"
# 返回迭代器
email_iter = re.finditer(r"\w+@\w+\.\w+", text)
# 遍历迭代器,提取内容
for match in email_iter:
print(match.group()) # 依次输出:a@163.com、b@qq.com
print(match.start()) # 输出匹配内容的起始位置(如a@163.com的起始索引是5)
print(match.end()) # 输出匹配内容的结束位置(如a@163.com的结束索引是13)
注意事项
- 迭代器只能遍历一次,再次遍历需重新调用
re.finditer
; - 若需获取匹配位置(起始 / 结束索引),
finditer
比findall
更方便(findall
只返回内容,不返回位置)。
二、替换与修改类:替换符合规则的内容
这类方法的核心是 "找到符合正则的内容并替换为新内容",常用于文本清洗、敏感词过滤等场景。
1. re.sub(pattern, repl, string, count=0, flags=0)
------ 替换匹配的内容
核心作用
遍历字符串,将 所有 符合正则的子串替换为 repl
(新内容),返回替换后的新字符串。
适合场景:敏感词过滤(如替换 "垃圾" 为 "***")、格式统一(如将所有 "-" 替换为 ".")。
语法与示例
repl
:替换的新内容(可以是字符串,也可以是函数);count
:可选参数,指定替换次数(默认0
表示替换所有)。
TypeScript
import re
# 示例1:敏感词过滤(替换"垃圾""广告"为"***")
text = "这条垃圾信息是广告,别信!"
clean_text = re.sub(r"垃圾|广告", "***", text)
print(clean_text) # 输出:这条***信息是***,别信!
# 示例2:指定替换次数(只替换前1个匹配)
text = "a b c d"
new_text = re.sub(r"\s", "-", text, count=1) # 只替换第一个空格
print(new_text) # 输出:a-b c d
# 示例3:repl是函数(动态替换,如将数字加1)
def add_one(match):
num = int(match.group()) # 提取匹配的数字
return str(num + 1) # 返回加1后的字符串
text = "订单金额:100元,优惠:20元"
new_text = re.sub(r"\d+", add_one, text)
print(new_text) # 输出:订单金额:101元,优惠:21元
注意事项
- 若
repl
是字符串,可使用\1
\2
引用正则中的分组(如re.sub(r"
(\w+)@(\w+)", r"
\1_替换_\2", text)
,将a@163
替换为a_替换_163
); count=0
表示替换所有,count=1
表示替换第一个,以此类推。
2. re.subn(pattern, repl, string, count=0, flags=0)
------ 替换 + 返回替换次数
核心作用
与 re.sub
功能完全一致,但返回的是 元组 :(替换后的新字符串, 替换次数)
。
适合场景:需要知道 "替换了多少次" 的场景(如统计文本中敏感词出现次数)。
TypeScript
import re
text = "垃圾信息1,垃圾信息2,广告信息"
# 返回 (新文本, 替换次数)
result = re.subn(r"垃圾|广告", "***", text)
print(result) # 输出:('***信息1,***信息2,***信息', 3)
print(result[0]) # 替换后的文本:***信息1,***信息2,***信息
print(result[1]) # 替换次数:3
三、分割字符串类:用正则分割字符串
re.split(pattern, string, maxsplit=0, flags=0)
------ 按正则规则分割字符串
核心作用
用 "符合正则的子串" 作为分隔符,将原字符串分割成列表(区别于 str.split
,str.split
只能按固定字符分割,re.split
可按灵活规则分割)。
适合场景:按 "任意空白字符"(空格、换行、制表符)分割、按 "多种分隔符" 分割(如 ,
;
|
)。
语法与示例
maxsplit
:可选参数,指定最大分割次数(默认0
表示分割所有)。
TypeScript
import re
# 示例1:按"任意空白字符"分割(空格、换行\n、制表符\t)
text = "Hello World\nPython\tJava" # 中间有3个空格、1个换行、1个制表符
result = re.split(r"\s+", text) # \s+ 表示1个或多个空白字符
print(result) # 输出:['Hello', 'World', 'Python', 'Java']
# 示例2:按"多种分隔符"分割(, 或 ; 或 |)
text = "a,b;c|d"
result = re.split(r",|;|\|", text) # | 是正则中的"或",需转义为 \|
print(result) # 输出:['a', 'b', 'c', 'd']
# 示例3:指定最大分割次数(只分割前2次)
text = "a,b,c,d"
result = re.split(r",", text, maxsplit=2)
print(result) # 输出:['a', 'b', 'c,d']
注意事项
-
若正则中有分组(
()
),分割结果会包含 "分隔符本身"。例如:TypeScripttext = "a,b,c" result = re.split(r"(,)", text) # 正则有分组,包含分隔符 print(result) # 输出:['a', ',', 'b', ',', 'c']
四、编译正则类:预编译正则表达式
re.compile(pattern, flags=0)
------ 编译正则为 Pattern 对象
核心作用
将正则表达式 pattern
编译成 Pattern 对象 ,后续可重复使用该对象调用 match
/search
/findall
等方法(无需重复解析正则,提升效率)。
适合场景:多次使用同一个正则表达式(如在循环中反复匹配同一规则)。
TypeScript
import re
# 1. 编译正则(只编译一次)
email_pattern = re.compile(r"\w+@\w+\.\w+") # 编译邮箱正则
# 2. 重复使用Pattern对象(多次匹配,无需重新编译)
text1 = "我的邮箱是a@163.com"
text2 = "他的邮箱是b@qq.com"
text3 = "她的邮箱是c@google.com"
print(email_pattern.findall(text1)) # 输出:['a@163.com']
print(email_pattern.findall(text2)) # 输出:['b@qq.com']
print(email_pattern.search(text3).group()) # 输出:c@google.com
注意事项
- 编译后的 Pattern 对象拥有与
re
模块同名的方法(如match
/search
/findall
),用法完全一致; - 若只使用一次正则,无需编译(直接用
re.xxx
更简洁);若多次使用,编译能显著提升效率(尤其是复杂正则)。
五、常用方法对比与适用场景总结
方法 | 核心功能 | 返回结果类型 | 适用场景 |
---|---|---|---|
re.match |
仅匹配字符串开头 | Match 对象 / None | 验证字符串开头格式(如手机号、身份证号) |
re.search |
全局查找第一个匹配 | Match 对象 / None | 查找任意位置的单个匹配(如找第一个邮箱) |
re.findall |
全局查找所有匹配 | 列表(字符串) | 批量提取内容(如所有手机号、所有链接) |
re.finditer |
全局查找所有匹配(迭代器) | 迭代器(Match 对象) | 大量匹配结果(节省内存)+ 获取匹配位置 |
re.sub |
替换所有匹配 | 新字符串 | 敏感词过滤、格式统一 |
re.subn |
替换 + 返回替换次数 | 元组(新字符串,次数) | 替换 + 统计次数(如统计敏感词数量) |
re.split |
按正则分割字符串 | 列表(字符串) | 灵活分割(如任意空白、多种分隔符) |
re.compile |
预编译正则 | Pattern 对象 | 多次使用同一正则(提升效率) |