正则表达式是一种特殊的字符序列,用于匹配或查找其他字符串或字符串集合,它使用一种专门的语法来定义模式。正则表达式通常简称为 regex 或 regexp。
这类模式常被字符串搜索算法用于“查找”或“查找并替换”操作,或者用于输入验证。
在数据科学项目中进行大规模文本处理时,经常需要对文本数据进行操作。包括 Python 在内的许多编程语言都支持正则表达式处理。Python 标准库中的 re 模块提供了这一功能。
由于 re 模块中的大多数函数都使用原始字符串(raw strings),我们首先需要了解什么是原始字符串。
原始字符串(Raw Strings)
正则表达式使用反斜杠 \ 来表示特殊形式,或允许在不触发其特殊含义的情况下使用特殊字符。而 Python 本身也使用 \ 作为转义字符。因此,为了避免冲突,Python 推荐使用原始字符串表示法。
在字符串前加上 r 或 R 前缀即可将其变为原始字符串:
>>> normal = "Hello"
>>> print(normal)
Hello
>>> raw = r"Hello"
>>> print(raw)
Hello
在一般情况下,两者没有区别。但当字符串中包含转义字符时,普通字符串会解释转义序列,而原始字符串则不会:
>>> normal = "Hello\nWorld"
>>> print(normal)
Hello
World
>>> raw = r"Hello\nWorld"
>>> print(raw)
Hello\nWorld
在上面的例子中,普通字符串打印时,\n 被解释为换行符;而原始字符串因前缀 r 的存在,\n 被原样输出。
元字符(Metacharacters)
大多数字母和字符都能直接匹配自身,但某些字符是元字符(metacharacters),具有特殊含义,不会匹配自身。常见的元字符包括:
. ^ $ * + ? { } [ ] \ | ( )
方括号 [ 和 ] 表示一个字符集合,可以列出单个字符,也可以用 - 表示范围。
| 序号 | 元字符 & 说明 |
|---|---|
| 1 | [abc]:匹配 a、b 或 c 中任意一个字符 |
| 2 | [a-c]:等价于 [abc] |
| 3 | [a-z]:仅匹配小写字母 |
| 4 | [0-9]:仅匹配数字 |
| 5 | [^5]:匹配除 5 以外的任意字符(^ 在方括号内表示取反) |
反斜杠 \ 是转义元字符。若要匹配 [ 或 \ 本身,需在其前加反斜杠:$$ 或 \\。
以下是以 \ 开头的预定义字符集(特殊序列):
| 序号 | 特殊序列 & 说明 |
|---|---|
| 1 | \d:匹配任意十进制数字,等价于 [0-9] |
| 2 | \D:匹配任意非数字字符,等价于 [^0-9] |
| 3 | \s:匹配任意空白字符,等价于 [\t\n\r\f\v] |
| 4 | \S:匹配任意非空白字符 |
| 5 | \w:匹配任意字母、数字或下划线,等价于 [a-zA-Z0-9_] |
| 6 | \W:匹配任意非字母数字字符 |
| 7 | .:匹配除换行符 \n 外的任意单个字符 |
| 8 | ?:匹配其左侧模式 0 次或 1 次 |
| 9 | +:匹配其左侧模式 1 次或多次 |
| 10 | *:匹配其左侧模式 0 次或多次 |
| 11 | \b:单词边界;\B 为其反义 |
| 12 | [..]:匹配方括号内的任意单个字符;[^..] 匹配不在其中的字符 |
| 13 | \:用于转义特殊字符,如 \. 匹配句点,\+ 匹配加号 |
| 14 | {n,m}:匹配前一个模式至少 n 次、至多 m 次 |
| 15 | a|b:匹配 a 或 b |
Python 的 re 模块常用函数
re.match() 函数
尝试从字符串开头匹配正则表达式模式。
语法:
re.match(pattern, string, flags=0)
参数说明:
| 参数 | 说明 |
|---|---|
| pattern | 要匹配的正则表达式 |
| string | 要搜索的字符串 |
| flags | 可选标志(如 re.I 表示忽略大小写) |
返回值: 成功返回 Match 对象,失败返回 None。
Match 对象方法:
.start():匹配起始位置.end():匹配结束位置.group(num=0):返回整个匹配内容(或指定子组).groups():返回所有子组组成的元组
示例:
import re
line = "Cats are smarter than dogs"
matchObj = re.match(r'Cats', line)
print(matchObj.start(), matchObj.end()) # 输出: 0 4
print("matchObj.group() :", matchObj.group()) # 输出: Cats
re.search() 函数
在整个字符串中搜索第一个匹配项(不限于开头)。
语法:
re.search(pattern, string, flags=0)
示例:
import re
line = "Cats are smarter than dogs"
matchObj = re.search(r'than', line)
print(matchObj.start(), matchObj.end()) # 输出: 17 21
print("matchObj.group() :", matchObj.group()) # 输出: than
匹配(match) vs 搜索(search)
match()仅在字符串开头匹配。search()在字符串任意位置搜索第一个匹配。
示例:
import re
line = "Cats are smarter than dogs"
matchObj = re.match(r'dogs', line, re.M|re.I)
if matchObj:
print("match -->", matchObj.group())
else:
print("No match!!") # 输出此行
searchObj = re.search(r'dogs', line, re.M|re.I)
if searchObj:
print("search -->", searchObj.group()) # 输出: dogs
else:
print("Nothing found!!")
输出:
No match!!
search --> dogs
re.findall() 函数
返回字符串中所有不重叠的匹配项,以列表形式返回。
语法:
re.findall(pattern, string, flags=0)
示例 1:
import re
string = "Simple is better than complex."
obj = re.findall(r"ple", string)
print(obj) # ['ple', 'ple']
示例 2(提取单词):
obj = re.findall(r"\w*", string)
print(obj) # ['Simple', '', 'is', '', 'better', '', 'than', '', 'complex', '', '']
注意:
\w*会匹配空字符串(因为*表示 0 次或多次),所以结果中有很多空字符串。
re.sub() 函数
用于替换匹配的字符串。
语法:
re.sub(pattern, repl, string, count=0)
count:最大替换次数(默认 0 表示全部替换)
示例 1(删除注释):
import re
phone = "2004-959-559 # This is Phone Number"
num = re.sub(r'#.*$', "", phone)
print("Phone Num :", num) # 2004-959-559
示例 2(只保留数字):
num = re.sub(r'\D', "", phone)
print("Phone Num :", num) # 2004959559
示例 3(单词替换):
string = "Simple is better than complex. Complex is better than complicated."
obj = re.sub(r'is', r'was', string)
print(obj)
# 输出: Simple was better than complex. Complex was better than complicated.
re.compile() 函数
将正则表达式编译为正则对象,便于重复使用,提高效率。
语法:
re.compile(pattern, flags=0)
常用标志(flags):
| 标志 | 说明 |
|---|---|
re.I |
忽略大小写 |
re.L |
使用当前区域设置(locale) |
re.M |
多行模式:^ 和 $ 匹配每行开头/结尾 |
re.S |
单行模式:. 可匹配换行符 |
re.U |
使用 Unicode 字符集(影响 \w, \b 等) |
re.X |
允许更清晰的正则写法(忽略空白和 # 注释) |
示例:
import re
string = "Simple is better than complex. Complex is better than complicated."
pattern = re.compile(r'is')
# 使用编译后的对象
print(pattern.search(string).span()) # (7, 9)
print(pattern.findall(string)) # ['is', 'is']
print(pattern.sub('was', string))
# 输出: Simple was better than complex. Complex was better than complicated.
re.finditer() 函数
返回一个迭代器,逐个生成所有匹配的 Match 对象。
语法:
re.finditer(pattern, string, flags=0)
示例:
import re
string = "Simple is better than complex. Complex is better than complicated."
pattern = re.compile(r'is')
for match in pattern.finditer(string):
print(match.span())
# 输出:
# (7, 9)
# (39, 41)
正则表达式的典型应用场景
查找所有副词(以 ly 结尾的单词)
import re
text = "He was carefully disguised but captured quickly by police."
obj = re.findall(r"\w+ly\b", text)
print(obj) # ['carefully', 'quickly']
查找以元音字母开头的单词
import re
text = 'Errors should never pass silently. Unless explicitly silenced.'
obj = re.findall(r'\b[aeiouAEIOU]\w+', text)
print(obj) # ['Errors', 'Unless', 'explicitly']
正则表达式修饰符(Flags)
| 修饰符 | 说明 |
|---|---|
re.I |
忽略大小写 |
re.L |
使用本地化设置 |
re.M |
多行模式(^ 和 $ 匹配每行) |
re.S |
单行模式(. 匹配包括换行符在内的任意字符) |
re.U |
Unicode 模式 |
re.X |
扩展模式(忽略空格和 # 注释) |
常用正则表达式模式汇总
| 序号 | 模式 | 说明 |
|---|---|---|
| 1 | ^ |
匹配行首 |
| 2 | $ |
匹配行尾 |
| 3 | . |
匹配除换行符外的任意字符 |
| 4 | [...] |
匹配括号内任意一个字符 |
| 5 | [^...] |
匹配不在括号内的任意字符 |
| 6 | re* |
匹配 0 次或多次 |
| 7 | re+ |
匹配 1 次或多次 |
| 8 | re? |
匹配 0 次或 1 次 |
| 9 | re{n} |
精确匹配 n 次 |
| 10 | re{n,} |
至少匹配 n 次 |
| 11 | re{n,m} |
匹配 n 到 m 次 |
| 12 | a|b |
匹配 a 或 b |
| 13 | (re) |
分组并记住匹配内容 |
| 14–18 | (?imx:...) |
局部启用/禁用标志 |
| 19 | (?#...) |
注释 |
| 20 | (?=re) |
正向先行断言 |
| 21 | (?!re) |
负向先行断言 |
| 23–28 | \w, \W, \s, \S, \d, \D |
预定义字符类 |
| 29 | \A |
匹配字符串开头 |
| 30–31 | \Z, \z |
匹配字符串结尾 |
| 33–34 | \b, \B |
单词边界 / 非单词边界 |
| 36–37 | \1...\9, \10 |
反向引用 |
示例分类
字面量字符
python→ 匹配 "python"
字符类
[Pp]ython→ 匹配 "Python" 或 "python"[aeiou]→ 匹配任意小写元音[^0-9]→ 匹配非数字
特殊字符类
\d→ 数字\w→ 字母/数字/下划线\s→ 空白字符
重复
ruby?→ "rub" 或 "ruby"\d{3,5}→ 3~5 位数字
非贪婪匹配
<.*?>→ 匹配最短的<...>(如<python>而非<python>perl>)
分组
([Pp]ython(, )?)+→ 匹配 "Python", "Python, python" 等
反向引用
(['"])[^\1]*\1→ 匹配带引号的字符串(单引号或双引号)
锚点(位置匹配)
^Python→ 行首的 "Python"\bPython\b→ 完整单词 "Python"Python(?=!)→ 后面紧跟!的 "Python"
特殊语法
R(?i:uby)→ 在局部启用忽略大小写rub(?:y|le)→ 分组但不捕获(无反向引用)
正则表达式是文本处理的强大工具,掌握其核心语法和 re 模块的使用,能极大提升 Python 文本处理能力。