实战一:解析 Nginx 访问日志
import re from collections import Counter # Nginx 默认日志格式: # 127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /index.html HTTP/1.0" 200 2326 LOG_PATTERN = re.compile(r""" (?P<ip>\S+)\s+ # IP 地址 \S+\s+ # ident(通常为 -) (?P<user>\S+)\s+ # 认证用户 \[(?P<time>[^\]]+)\]\s+ # 时间 "(?P<method>\w+)\s+ # HTTP 方法 (?P<path>\S+)\s+ # 请求路径 (?P<proto>\S+)"\s+ # 协议 (?P<status>\d{3})\s+ # 状态码 (?P<size>\d+|-) # 响应大小 """, re.VERBOSE) def parse_log(log_file): status_counter = Counter() with open(log_file) as f: for line in f: m = LOG_PATTERN.match(line) if m: status_counter[m.group('status')] += 1 return status_counter
实战二:提取并清理 HTML
# 提取纯文本(移除 HTML 标签) def strip_html(html): # 先移除 script 和 style 块 text = re.sub(r'<(?:script|style)[^>]*>.*?</(?:script|style)>', '', html, flags=re.DOTALL | re.IGNORECASE) # 再移除所有标签 text = re.sub(r'<[^>]+>', '', text) # 合并多余空白 text = re.sub(r'\s+', ' ', text).strip() return text # 提取所有链接 def extract_links(html): return re.findall(r'href="([^"]*)"', html, re.IGNORECASE) # 将相对链接转为绝对链接 def absolutize_links(html, base_url): return re.sub( r'href="(?!https?://)([^"]*)"', lambda m: f'href="{base_url}/{m.group(1).lstrip("/")}"', html )
实战三:处理 CSV 数据
# 解析 CSV(处理引号内含逗号的情况) CSV_FIELD = re.compile(r'"(?:[^"]|"")*"|[^,]+') def parse_csv_line(line): fields = CSV_FIELD.findall(line) result = [] for f in fields: if f.startswith('"'): f = f[1:-1].replace('""', '"') # 去引号,处理转义 result.append(f.strip()) return result # 示例 parse_csv_line('name,"Doe, John",age,30') # → ['name', 'Doe, John', 'age', '30']
实战四:代码中的批量重构
# 将 Python 2 print 语句改为 Python 3 函数 def fix_print(code): return re.sub( r'^(\s*)print\s+(.+)$', r'\1print(\2)', code, flags=re.MULTILINE ) # 将驼峰命名改为下划线命名 def camel_to_snake(name): s1 = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', name) return re.sub(r'([a-z\d])([A-Z])', r'\1_\2', s1).lower() camel_to_snake('getUserName') # → 'get_user_name' camel_to_snake('parseHTTPRequest') # → 'parse_http_request'
实战五:模板渲染
# 简单模板引擎:替换 {{变量名}} def render_template(template, variables): def replacer(m): key = m.group(1).strip() return str(variables.get(key, m.group(0))) return re.sub(r'\{\{\s*(\w+)\s*\}\}', replacer, template) template = "Hello, {{name}}! You have {{count}} messages." render_template(template, {'name': 'Alice', 'count': 5}) # → "Hello, Alice! You have 5 messages."
性能优化技巧
1. 预编译常用模式
# ❌ 每次调用都重新编译 for line in lines: re.search(r'\d+', line) # ✅ 预编译,复用 DIGIT = re.compile(r'\d+') for line in lines: DIGIT.search(line)
2. 优先用否定字符类代替懒惰量词
# 较慢(有回溯) ".*?" # 更快(无回溯) "[^"]*"
3. 避免嵌套量词
# 危险,可能造成 ReDoS (\w+\s*)+ # 安全替代 \w+(?:\s+\w+)*
4. 善用 fullmatch 替代 ^ $
# 验证格式时,fullmatch 更清晰高效 re.fullmatch(r'\d{4}', s) # 推荐 re.match(r'^\d{4}$', s) # 等价但冗余
调试正则表达式
- regex101.com:在线测试,实时高亮,支持多种方言
- regexr.com:可视化解释正则各部分含义
- pythex.org:专为 Python 正则设计的在线测试
- Python
re.DEBUG标志:打印正则内部解析树
import re re.compile(r'(\d+)-(\w+)', re.DEBUG) # SUBPATTERN 1 0 0 # MAX_REPEAT 1 MAXREPEAT # IN # RANGE (48, 57) ← 数字 0-9 # LITERAL 45 ← - # ...
课程总结
恭喜完成正则表达式完全指南!你现在掌握了:
- 字符、量词、字符类、边界的完整语法
- 捕获组、命名组、反向引用
- 正向/负向前瞻与后顾断言
- 贪婪/懒惰量词与回溯机制(含灾难性回溯的规避)
- 标志位:i、m、s、x 的作用
- Python re 模块与 JavaScript RegExp 的完整 API
- 常用模式库与真实项目实战