python正则表达式实例代码

📅 发布时间:2026/10/15 11:03:45
python正则表达式实例代码
前言正则表达式regular expression简称 regex是处理文本的通用工具在一堆字符里找出满足某种形状的片段或者把符合形状的片段替换成别的内容。Python 通过标准库re模块提供这套能力不需要安装任何第三方包。初学者最常见的误解有两个。第一以为re.match()会在整段文字里「到处找」——其实它只从字符串开头尝试匹配找不找得到都不看后面。第二以为写了正则就一定比手写字符串处理快——正则有回溯backtracking开销写得不好可能比直接判断还慢具体差多少要看数据和模式不能用固定倍数概括。本文用一批可以直接复制进.py文件运行的实例把「查找、提取、替换、校验」四类常见需求串起来并在最后集中列出几个真会踩到的坑。所有示例基于 Python 3.8 编写用到的特性都会标注版本。另外提醒Python 2.7 已于 2020 年 1 月 1 日停止维护它在re上按 bytes 语义处理字符串、还需要u...前缀来得到 Unicode 字符串unicode、basestring类型在 Python 3 里也已不存在旧教程的写法不要照搬。一、最常用的四个动作re模块的模块级函数module-level function是四个动作最直接的写法动作函数返回值说明找第一个re.search(pattern, string)Match或None扫描整串任意位置命中即可从开头匹配re.match(pattern, string)Match或None只从下标 0 开始找全部re.findall(pattern, string)列表返回所有不重叠匹配的文本替换re.sub(pattern, repl, string)新字符串原字符串不变返回新串用一句话记住match和search的区别re.match(c, abcdef)返回None而re.search(c, abcdef)返回一个Match对象官方文档的search() vs. match()一节给的就是这个例子。要在「整串完全符合」时才成功还可以用re.fullmatch()它需要 Python 3.4 及以上。下面的实例覆盖手机号、邮箱、日期、中文和空白折叠# 适用于 Python 3.8import retext 联系邮箱aliceexample.com手机 13800138000备用 010-88886666# 找所有邮箱地址emails re.findall(r[A-Za-z0-9._%-][A-Za-z0-9.-]\.[A-Za-z]{2,}, text)print(emails) # [aliceexample.com]# 找所有 11 位手机号1 开头第二位 3-9后面再跟 9 位数字phones re.findall(r1[3-9]\d{9}, text)print(phones) # [13800138000]# 把空白字符折叠成单个空格collapsed re.sub(r\s, , text)print(collapsed)\d在默认的 Unicodestr模式下匹配「任意 Unicode 十进制数字」范围比[0-9]大在 bytes 模式或加了re.ASCII时才等价于[0-9]。上面的字符串里只有 ASCII 数字所以\d和[0-9]结果一样但换成含全角数字的文本时两者就会分叉。二、分组把一次匹配拆成几块圆括号(...)表示一个捕获组capturing group匹配完成后可以用Match.group()按序号或名字取出每一段。# 适用于 Python 3.8import reline 2024-05-01 上线2024-06-15 回滚m re.search(r(\d{4})-(\d{2})-(\d{2}), line)if m:print(m.group(0)) # 2024-05-01整个匹配print(m.group(1)) # 2024第一个括号print(m.group(2)) # 05print(m.group(3)) # 01print(m.groups()) # (2024, 05, 01)print(m.span()) # (0, 10)匹配在原串里的起止下标Match.span()返回(start, end)二元组m.group()等价于m.group(0)。除此之外还有两个常用方法m.groups(defaultNone)返回从第 1 组到最后一组的元组没参与匹配的组用default填充。m.groupdict(defaultNone)只针对命名组返回「名字到内容」的字典。命名组用(?Pname...)写法例如(?Pyear\d{4})。findall对分组很敏感没有分组时返回整段匹配文本只有一个分组时返回该分组的内容有多个分组时返回元组。所以想拿「整段」就一定不要随便加括号或者改用finditer逐个处理Match对象。# 适用于 Python 3.8import resrc 宽度20 高度10print(re.findall(r\w\d, src)) # [宽度20, 高度10]无分组print(re.findall(r(\w)(\d), src)) # [(宽度, 20), (高度, 10)]# 想要每段匹配的起止位置用 finditerfor m in re.finditer(r(\w)(\d), src):print(m.group(1), m.group(2), m.span())三、替换字符串或函数都行re.sub(pattern, repl, string, count0)的repl既可以是字符串也可以是函数。是字符串时\1、\gname这类反向引用backreference会被替换成对应分组的内容是函数时函数接收一个Match对象返回什么就替换成什么。count0默认表示全部替换正数表示最多替换几次。# 适用于 Python 3.8import retext 手机 13800138000 与 13912345678 都已登记# repl 用函数把中间四位打码def mask(m):s m.group(0)return s[:3] **** s[-4:]print(re.sub(r1[3-9]\d{9}, mask, text))# 手机 138****8000 与 139****5678 都已登记# repl 用字符串 反向引用交换键值对顺序kv name:aliceprint(re.sub(r(\w):(\w), r\2:\1, kv)) # alice:name如果替换内容来自用户输入、本身可能含反斜杠不要把它直接当repl用re.escape()是为「模式」转义的不能用在替换串上。稳妥做法是把替换逻辑写成函数返回原样的字符串避免\1之类被误解释。四、提取中文与清洗中文字符在 Unicode 里的基本区间是一到鿿CJK 统一表意文字配合可以连续取出中文片段# 适用于 Python 3.8import res 品牌华为 Mate60 Pro产地中国深圳print(re.findall(r[一-鿿], s)) # [品牌, 华为, 产地, 中国深圳]# 只保留字母、数字和下划线去掉其它符号raw a b!c#d$e%f^gh*iprint(re.sub(r[^0-9A-Za-z_], , raw)) # abcdefghi第二个例子用「取反」字符集[^...]表达「除了这些都不要」是清洗文本的常用套路。注意^只有在方括号内的第一个位置才表示取反放在别处它就是普通的^字符。常见坑点❌ 用re.match()在长文本里找任意位置的内容结果永远None。✅ 从任意位置找用re.search()只在开头找才用re.match()。❌ 写re.findall((abc), s)却发现返回的是[abc]这种分组内容、以为拿错了。✅ 记住findall有分组时返回分组要整段匹配就删掉多余的括号或改用re.finditer()。❌ 模式里写\d而不是r\d在含\1、\b等序列时被 Python 字符串先解释一遍行为诡异。✅ 正则模式一律加r前缀写成r\d让反斜杠原样交给re。❌ 以为.能匹配换行用r.去匹配多行文本却只拿到第一行。✅ 默认.不匹配换行需要时加re.DOTALL也可写成(?s)。❌ 用贪心的rli(.*)/li去提取多个列表项结果一次吞掉了从第一个li到最后一个/li的整块。✅ 改成懒惰的rli(.*?)/li量词加?后「尽量少匹配」。❌ 用r[0-9]与r\d混着写却期待它们在含全角数字的文本里得到同样结果。✅ 默认 Unicode 模式下\d比[0-9]范围大要限定 ASCII 就写[0-9]或加re.ASCII。❌ 用re.split(r(\W), s)后发现结果列表里多出了分隔符本身。✅ 这是文档规定的行为分隔模式里若有捕获组分组内容会一并保留在结果中不想要就写成非捕获组(?:...)。❌ 把手机号、身份证这类正则在整段文本上直接findall忽略了它还会匹配到更长的数字串中间一段。✅ 用\b或(?!\d)、(?!\d)之类的边界断言把前后卡住避免「切到别人一半」。总结需求推荐函数关键点从任意位置找第一个re.search命中即返回Match否则None判断整串是否符合re.fullmatch需 Python 3.4取全部分段re.finditergroup()比findall更好控制分组替换re.subrepl可为字符串或函数按模式切分re.split捕获组会保留在结果里把「找、取、换、切」四类动作对应到search、finditer、sub、split再牢记模式用r...、match只从开头、.不跨行大部分日常文本处理就能写对。真要抠性能请用timeit在自己的数据上实测而不是相信某个固定的「提速几倍」说法。