正则表达式的应用
1. 正则表达式概述
处理文本时,常需要按某种“规则”检查、提取或改写片段,例如从日志里找出订单号、校验表单字段格式、把敏感数字中间位打码。正则表达式(Regular Expression)就是描述这类规则的专用记法:它定义一种模式,用来判断字符串是否匹配,或从字符串中取出/替换匹配部分。
可以把它理解成比文件名通配符(*、?)更精细的匹配语言:表达力更强,书写成本也更高。今天绝大多数语言都内置了正则支持;在 Python 中,标准库模块re负责编译与匹配。
更多语法可参考通用入门材料,例如 正则表达式 30 分钟入门。读完后,下表中的常用符号会更容易对照使用。
| 符号 | 含义 | 示例 | 说明 |
|---|---|---|---|
. | 任意单个字符 | c.t | 可匹配cat、c9t等 |
\w | 字母、数字、下划线 | \w+ | 一段“词”字符 |
\s | 空白(空格、制表、换行等) | id\s*= | id与=之间允许空白 |
\d | 数字 | \d{4} | 恰好 4 位数字 |
\b | 单词边界 | \bEND\b | 独立单词END |
^/$ | 串首 / 串尾 | ^ERR、\.log$ | 以 ERR 开头、以 .log 结尾 |
\W\S\D\B | 与上面几类相反 | \D+ | 连续非数字 |
[...]/[^...] | 字符集 / 取反 | [A-Z]、[^0-9] | 大写字母;非数字 |
*/+/? | 0 次及以上 / 1 次及以上 / 0 或 1 次 | \d+ | 至少一位数字 |
{n}{m,}{m,n} | 固定或区间次数 | \d{2,4} | 2~4 位数字 |
| | 分支(或) | GET|POST | 匹配其一 |
(exp) | 捕获分组 | (\d+)-(\d+) | 可取各组内容 |
(?:exp) | 非捕获分组 | (?:ab)+ | 只分组不捕获 |
(?=exp)/(?<=exp) | 正向 / 反向先行断言 | \d+(?=元) | 数字后紧跟“元” |
(?!exp)/(?<!exp) | 负向断言 | (?<!\d)\d{6}(?!\d) | 恰好 6 位且两侧非数字 |
*?+???{m,n}? | 懒惰量词 | a.*?b | 尽可能短地匹配 |
说明:若要匹配正则元字符本身,需转义,例如字面小数点写作
\.,字面括号写作\(\)。
2. Python 中的re模块
| 函数 / 标记 | 作用 |
|---|---|
compile(pattern, flags=0) | 编译模式,得到可复用的正则对象 |
match(pattern, string, flags=0) | 从字符串开头尝试匹配,成功返回匹配对象,否则None |
fullmatch(pattern, string, flags=0) | 要求整串完全符合模式 |
search(pattern, string, flags=0) | 在串中查找第一次出现 |
findall(pattern, string, flags=0) | 返回所有匹配的字符串列表(有分组时结构会变化) |
finditer(pattern, string, flags=0) | 返回匹配对象的迭代器 |
split(pattern, string, maxsplit=0, flags=0) | 按模式切分,返回列表 |
sub(pattern, repl, string, count=0, flags=0) | 用替换内容替换匹配;repl可为字符串或函数 |
purge() | 清理隐式编译缓存 |
re.I/re.IGNORECASE | 忽略大小写 |
re.M/re.MULTILINE | 多行模式下^$作用于每行 |
关键概念:
- 同一模式若会多次使用,应先
compile,再调用对象的match/search/findall等方法。 - 书写模式时推荐原始字符串
r'...',避免\被 Python 字符串先转义一层(否则\d常要写成\\d)。
多个flags可用按位或组合,例如flags=re.I | re.M。
3. 应用示例
3.1 校验工号与邮箱(match/fullmatch)
规则约定:
- 工号:2 位大写字母 + 4~6 位数字,整串合法(如
HR1024、IT90001)。 - 邮箱:本地部分允许字母数字、点、下划线、百分号、加号、减号;
@后为域名片段。
importre staff_id=input('工号: ').strip()email=input('邮箱: ').strip()# match:从开头匹配;配合 ^...$ 等价于“整串符合”m_id=re.match(r'^[A-Z]{2}\d{4,6}$',staff_id)ifnotm_id:print('工号格式无效')# fullmatch:整串必须完全匹配,模式里可不写 ^ $m_mail=re.fullmatch(r'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}',email,)ifnotm_mail:print('邮箱格式无效')ifm_idandm_mail:print('校验通过')提示:
match只锚定开头;若未写$,AB1234xyz在宽松模式下可能被误判为“从开头已匹配成功”。校验整字段时优先fullmatch,或显式使用^...$。
3.2 从日志中提取订单号(compile/findall/finditer/search)
订单号形如ORD-+ 8 位数字。用前后断言避免与更长数字串粘连,并演示三种取结果的方式。
importre# 左侧非数字、右侧非数字,降低误匹配pattern=re.compile(r'(?<!\d)ORD-\d{8}(?!\d)')log_text=""" [INFO] create ORD-20250101 ok retry ORD-20250102; noise ORD-20250102111 ignored client sent ORD-20250315 and ORD-20250316 """# 方法一:全部匹配成列表print(re.findall(pattern,log_text))# 方法二:迭代匹配对象forminpattern.finditer(log_text):print(m.group(),'at',m.start())# 方法三:search 从上次结束位置继续扫m=pattern.search(log_text)whilem:print(m.group())m=pattern.search(log_text,m.end())关键点:
findall适合只要文本;需要位置或分组细节时用finditer。search(..., pos)可从指定下标继续搜,适合手写扫描循环。
若号段规则更复杂,可把主体改成分支,例如ORD-(?:20\d{6}|9\d{7}),再保留两侧断言。
3.3 脱敏证件号与手机号(sub)
将 11 位手机号中间 4 位、18 位证件号中间若干位替换为*。repl使用函数,按分组拼接。
importredefmask_sensitive(text:str)->str:defrepl_phone(m:re.Match)->str:s=m.group()returns[:3]+'****'+s[7:]defrepl_id(m:re.Match)->str:s=m.group()returns[:4]+'*'*10+s[-4:]text=re.sub(r'(?<!\d)1\d{10}(?!\d)',repl_phone,text)text=re.sub(r'(?<!\d)\d{17}[\dXx](?!\d)',repl_id,text,flags=re.IGNORECASE,)returntext sample='联系人 13812345678,证件 110101199001011234,备用 13900001111'print(mask_sensitive(sample))# 输出类似:联系人 138****5678,证件 1101**********1234,备用 139****1111说明:
flags=re.I(或re.IGNORECASE)让证件末位x/X都能匹配。多个标志用|叠加。真实业务号段、校验位规则更严,此处仅演示替换接口。
3.4 按多种分隔符拆分配置行(split)
配置项可能用逗号、分号或竖线分隔,分隔符后允许空格。
importre line='host=10.0.0.8; port=8080 | timeout=3, retries=2'parts=re.split(r'[,;|]\s*',line)fields=[pforpinpartsifp]foriteminfields:print(item)# host=10.0.0.8# port=8080# timeout=3# retries=2split得到的列表可能含空串(首尾或连续分隔符),用列表推导过滤即可。需要限制段数时传入maxsplit。
总结
| 场景 | 常用接口 |
|---|---|
| 整字段是否合法 | fullmatch,或match+^...$ |
| 取出全部片段 | findall/finditer |
| 从某位置继续找 | search+ 结束下标 |
| 改写匹配内容 | sub(字符串或回调) |
| 按模式切开 | split |
| 模式复用 | 先compile |
正则擅长结构化文本的校验、抽取与改写;模式越复杂,越建议用在线调试工具边写边测。业务规则变更时,优先收紧字符类与断言,并补上边界用例(空串、粘连数字、大小写),避免“能跑但误伤”。
`)