ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

HoRain云--Python正则表达式完全指南:从基础到高性能匹配

HoRain云--Python正则表达式完全指南:从基础到高性能匹配

正则表达式是文本处理的利器,但写不好会带来性能问题和难以调试的Bug。本文从基础语法到高级技巧,再到性能优化和常见陷阱,系统讲解Python正则。

一、re模块基础

python

复制

下载

import re text = "我的电话是 138-1234-5678,邮箱 test@example.com" # 查找第一个 m = re.search(r"\d{3}-\d{4}-\d{4}", text) if m: print(m.group()) # 138-1234-5678 print(m.span()) # (7, 20) # 查找全部 phones = re.findall(r"\d{3}-\d{4}-\d{4}", text) # 匹配开头 if re.match(r"我的", text): print("以'我的'开头") # 全字符串匹配 if re.fullmatch(r"\d+", "12345"): print("全是数字") # 替换 new = re.sub(r"\d{4}", "****", text) # 分割 parts = re.split(r"[,,;;]", "a,b;c,d")

二、编译正则提升性能

重复使用同一正则时,预编译:

python

复制

下载

import re # 不推荐:每次调用都编译 def bad(): for line in lines: if re.search(r"ERROR|WARN", line): ... # 推荐:预编译 PATTERN = re.compile(r"ERROR|WARN") def good(): for line in lines: if PATTERN.search(line): ...

re模块内部有缓存(默认512个),但显式编译更清晰。

三、核心语法速查

python

复制

下载

# 字符类 r"[abc]" # a或b或c r"[^abc]" # 非a、b、c r"[a-z]" # 小写字母 r"\d" # 数字 [0-9] r"\w" # 单词字符 [a-zA-Z0-9_] r"\s" # 空白 # 量词 r"a*" # 0或多次 r"a+" # 1或多次 r"a?" # 0或1次 r"a{3}" # 恰好3次 r"a{2,5}" # 2到5次 r"a{2,}" # 至少2次 r"a+?" # 非贪婪 # 锚点 r"^start" # 行首 r"end$" # 行尾 r"\bword\b" # 单词边界 # 分组 r"(\d+)-(\d+)" # 捕获组 r"(?:abc)+" # 非捕获组 r"(?P<year>\d{4})" # 命名组

四、分组与命名组

python

复制

下载

import re pattern = re.compile( r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})" ) m = pattern.search("日期: 2024-01-15") print(m.group("year")) # 2024 print(m.groupdict()) # {'year': '2024', 'month': '01', 'day': '15'} # 反向引用 re.sub(r"(\w+)\s+\1", r"\1", "hello hello world") # "hello world"

五、前后向断言

python

复制

下载

# 前瞻:匹配后面是...的位置 r"foo(?=bar)" # foo后面是bar r"foo(?!bar)" # foo后面不是bar # 后顾:匹配前面是... r"(?<=foo)bar" # bar前面是foo r"(?<!foo)bar" # bar前面不是foo # 提取价格:数字后面是元 re.findall(r"\d+(?=元)", "苹果5元,香蕉3元") # ['5', '3']

注意:Python后顾断言必须是固定长度。

六、常用实战模式

6.1 邮箱校验

python

复制

下载

EMAIL = re.compile( r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" )

6.2 URL提取

python

复制

下载

URL = re.compile( r"https?://[^\s<>\"']+|www\.[^\s<>\"']+" )

6.3 手机号(中国大陆)

python

复制

下载

PHONE = re.compile(r"^1[3-9]\d{9}$")

6.4 日志解析

python

复制

下载

LOG = re.compile( r"(?P<ip>\d+\.\d+\.\d+\.\d+) " r"- - \[(?P<time>[^\]]+)\] " r"\"(?P<method>\w+) (?P<path>\S+) HTTP/[\d.]+\" " r"(?P<status>\d+) (?P<size>\d+)" )

6.5 HTML标签清理

python

复制

下载

def strip_html(text): return re.sub(r"<[^>]+>", "", text)

6.6 驼峰与下划线转换

python

复制

下载

def camel_to_snake(name): return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower() def snake_to_camel(name): parts = name.split("_") return parts[0] + "".join(p.capitalize() for p in parts[1:])

七、性能优化

7.1 避免灾难性回溯

python

复制

下载

# 危险:嵌套量词 r"(a+)+b" # 输入 "aaaaaaaaaaaaaaaaaaaaaac" 会指数级回溯 # 优化:原子组或占有量词 r"(?>a+)+b" # Python 3.11+ r"(a++)+b" # 占有量词

7.2 优先字符类而非或

python

复制

下载

# 慢 r"(cat|dog|bird)" # 快 r"(?:cat|dog|bird)" # 至少用非捕获组

7.3 用具体字符类替代.*

python

复制

下载

# 慢 r"<div>.*</div>" # 快 r"<div>[^<]*</div>"

7.4 合理使用re.DOTALL、re.MULTILINE

python

复制

下载

re.compile(r"^ERROR", re.MULTILINE) # ^匹配每行行首 re.compile(r"a.b", re.DOTALL) # .匹配换行 re.compile(r"abc", re.IGNORECASE) # 忽略大小写

八、常见陷阱

8.1 忘记原始字符串

python

复制

下载

# 错误:\d被当作转义 re.search("\d+", text) # 正确 re.search(r"\d+", text)

8.2re.matchvsre.search

  • match:从开头匹配

  • search:扫描整个字符串

  • fullmatch:必须完全匹配

python

复制

下载

re.match(r"\d+", "abc123") # None re.search(r"\d+", "abc123") # 匹配123

8.3 贪婪 vs 非贪婪

python

复制

下载

re.findall(r"<.*>", "<a><b>") # ['<a><b>'] re.findall(r"<.*?>", "<a><b>") # ['<a>', '<b>']

8.4 中文字符

python

复制

下载

r"[\u4e00-\u9fa5]+" # 中文字符范围

九、实战:日志分析器

python

复制

下载

import re from collections import Counter LOG_PATTERN = re.compile( r"(?P<ip>[\d.]+).*?" r"\"(?P<method>\w+) (?P<path>\S+).*?\" " r"(?P<status>\d{3})" ) def analyze(path): status_counter = Counter() path_counter = Counter() with open(path) as f: for line in f: m = LOG_PATTERN.search(line) if not m: continue status_counter[m.group("status")] += 1 path_counter[m.group("path")] += 1 return { "status": status_counter.most_common(10), "top_paths": path_counter.most_common(10), }

十、总结

正则表达式强大但需要谨慎使用。记住:预编译、原始字符串、具体字符类、避免嵌套量词。复杂解析任务(HTML、JSON)不要用正则,用专用解析器。用regex库可支持更高级特性如可变长度后顾。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进