Appearance
24|正则表达式
正则表达式(Regular Expression)是从文本中提取、匹配、替换特定模式的工具。日志分析、数据清洗、格式校验——这些场景都常用正则。
Python 的 re 模块提供正则支持。正则语法本身不是 Python 特有的,多数编程语言都有类似的实现。
一、基本匹配
python
import re
text = "hello world"
re.search("world", text) # <re.Match object; span=(6, 11), ...>
re.search("xyz", text) # None,找不到re.search() 在字符串中搜索第一个匹配。找到返回 Match 对象,找不到返回 None。
二、常用函数
| 函数 | 作用 |
|---|---|
re.search(pattern, text) | 搜索第一个匹配 |
re.match(pattern, text) | 从字符串开头匹配 |
re.findall(pattern, text) | 返回所有匹配的字符串列表 |
re.finditer(pattern, text) | 返回所有匹配的迭代器 |
re.sub(pattern, repl, text) | 替换匹配的内容 |
re.split(pattern, text) | 按匹配切分字符串 |
python
text = "2024-06-01 10:30:15 INFO 服务启动"
# 提取日期
re.search(r"\d{4}-\d{2}-\d{2}", text)
# <re.Match object; span=(0, 10), match='2024-06-01'>
# 提取所有数字
re.findall(r"\d+", text)
# ['2024', '06', '01', '10', '30', '15']
# 提取时间部分
match = re.search(r"(\d{2}):(\d{2}):(\d{2})", text)
match.group(0) # '10:30:15',整个匹配
match.group(1) # '10',第一个分组
match.group(2) # '30'
match.group(3) # '15'三、元字符
| 元字符 | 含义 |
|---|---|
. | 任意单个字符(除换行) |
\d | 数字 [0-9] |
\D | 非数字 |
\w | 单词字符 [a-zA-Z0-9_] |
\W | 非单词字符 |
\s | 空白字符(空格、制表符、换行) |
\S | 非空白字符 |
^ | 字符串开头 |
$ | 字符串结尾 |
\b | 单词边界 |
python
re.search(r"^hello", "hello world") # 匹配
re.search(r"^hello", "say hello") # 不匹配,不在开头
re.search(r"world$", "hello world") # 匹配
re.search(r"world$", "hello worlds") # 不匹配四、量词
| 量词 | 含义 |
|---|---|
* | 0 次或多次 |
+ | 1 次或多次 |
? | 0 次或 1 次 |
{n} | 恰好 n 次 |
{n,} | 至少 n 次 |
{n,m} | n 到 m 次 |
python
re.findall(r"\d+", "a1b23c456") # ['1', '23', '456']
re.findall(r"\d{2}", "a1b23c456") # ['23', '45']
re.findall(r"\d{2,3}", "a1b23c456") # ['23', '456']五、分组与命名分组
圆括号 () 创建分组:
python
pattern = r"(\d{4})-(\d{2})-(\d{2})"
match = re.search(pattern, "2024-06-01")
match.group(0) # '2024-06-01'
match.group(1) # '2024'
match.group(2) # '06'
match.group(3) # '01'命名分组 (?P<name>...):
python
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
match = re.search(pattern, "2024-06-01")
match.group("year") # '2024'
match.group("month") # '06'命名分组让代码更可读——不用记 group(1) 是年还是月。
六、替换
python
text = "password=abc123 token=xyz789"
# 把密码和 token 脱敏
re.sub(r"(password|token)=\S+", r"\1=***", text)
# 'password=*** token=***'\1 引用第一个分组的内容。\1=*** 表示"保留分组 1 的内容,后面接 =***"。
七、编译正则
频繁使用的正则可以先编译,提升性能:
python
pattern = re.compile(r"\d{4}-\d{2}-\d{2}")
pattern.search("2024-06-01")
pattern.search("2023-12-25")编译后的正则对象有和 re 模块相同的函数(search、findall、sub 等)。
八、原始字符串
正则表达式里反斜杠很多,用原始字符串 r"..." 避免双重转义:
python
# 不用原始字符串:要写成四个反斜杠才能匹配一个反斜杠
re.search("\\\\", "a\\b")
# 用原始字符串:直观
re.search(r"\\", "a\\b")写正则时始终用 r"...",除非有特殊理由不这样做。
九、使用边界
正则能跑通不等于准确。IP、邮箱、URL 这类格式要求严格时,表达式要按实际数据收紧:
python
# 太宽松,会误匹配
re.findall(r"\S+@\S+", "contact me at alice@example.com") # 能匹配
re.findall(r"\S+@\S+", "foo@bar") # 也会匹配(不是有效邮箱)
# 更严格的邮箱匹配(仍不完美)
pattern = r"[\w.+-]+@[\w.-]+\.\w+"正则不是万能工具。有结构化格式时优先用对应解析器(JSON 用 json,日期用 datetime,HTML 用 BeautifulSoup)。正则留给没有固定结构的自由文本。
记忆锚点:re.search() 找第一个,re.findall() 找所有,re.sub() 替换;\d 数字,\w 单词字符,\s 空白;() 分组,(?P<name>) 命名分组;* 0+,+ 1+,? 0/1,{n,m} n 到 m 次;正则始终用原始字符串 r"...";正则不是万能工具,结构化数据用专门解析器。