Skip to content

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 模块相同的函数(searchfindallsub 等)。

八、原始字符串

正则表达式里反斜杠很多,用原始字符串 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"...";正则不是万能工具,结构化数据用专门解析器。