小白python入门 - 23. Python 正则表达式的应用
正则表达式的应用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}24 位数字|分支或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, flags0)编译模式得到可复用的正则对象match(pattern, string, flags0)从字符串开头尝试匹配成功返回匹配对象否则Nonefullmatch(pattern, string, flags0)要求整串完全符合模式search(pattern, string, flags0)在串中查找第一次出现findall(pattern, string, flags0)返回所有匹配的字符串列表有分组时结构会变化finditer(pattern, string, flags0)返回匹配对象的迭代器split(pattern, string, maxsplit0, flags0)按模式切分返回列表sub(pattern, repl, string, count0, flags0)用替换内容替换匹配repl可为字符串或函数purge()清理隐式编译缓存re.I/re.IGNORECASE忽略大小写re.M/re.MULTILINE多行模式下^$作用于每行关键概念同一模式若会多次使用应先compile再调用对象的match/search/findall等方法。书写模式时推荐原始字符串r...避免\被 Python 字符串先转义一层否则\d常要写成\\d。多个flags可用按位或组合例如flagsre.I | re.M。3. 应用示例3.1 校验工号与邮箱match/fullmatch规则约定工号2 位大写字母 46 位数字整串合法如HR1024、IT90001。邮箱本地部分允许字母数字、点、下划线、百分号、加号、减号后为域名片段。importre staff_idinput(工号: ).strip()emailinput(邮箱: ).strip()# match从开头匹配配合 ^...$ 等价于“整串符合”m_idre.match(r^[A-Z]{2}\d{4,6}$,staff_id)ifnotm_id:print(工号格式无效)# fullmatch整串必须完全匹配模式里可不写 ^ $m_mailre.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# 左侧非数字、右侧非数字降低误匹配patternre.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 从上次结束位置继续扫mpattern.search(log_text)whilem:print(m.group())mpattern.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:sm.group()returns[:3]****s[7:]defrepl_id(m:re.Match)-str:sm.group()returns[:4]**10s[-4:]textre.sub(r(?!\d)1\d{10}(?!\d),repl_phone,text)textre.sub(r(?!\d)\d{17}[\dXx](?!\d),repl_id,text,flagsre.IGNORECASE,)returntext sample联系人 13812345678证件 110101199001011234备用 13900001111print(mask_sensitive(sample))# 输出类似联系人 138****5678证件 1101**********1234备用 139****1111说明flagsre.I或re.IGNORECASE让证件末位x/X都能匹配。多个标志用|叠加。真实业务号段、校验位规则更严此处仅演示替换接口。3.4 按多种分隔符拆分配置行split配置项可能用逗号、分号或竖线分隔分隔符后允许空格。importre linehost10.0.0.8; port8080 | timeout3, retries2partsre.split(r[,;|]\s*,line)fields[pforpinpartsifp]foriteminfields:print(item)# host10.0.0.8# port8080# timeout3# retries2split得到的列表可能含空串首尾或连续分隔符用列表推导过滤即可。需要限制段数时传入maxsplit。总结场景常用接口整字段是否合法fullmatch或match^...$取出全部片段findall/finditer从某位置继续找search 结束下标改写匹配内容sub字符串或回调按模式切开split模式复用先compile正则擅长结构化文本的校验、抽取与改写模式越复杂越建议用在线调试工具边写边测。业务规则变更时优先收紧字符类与断言并补上边界用例空串、粘连数字、大小写避免“能跑但误伤”。)