python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python正则处理带换行符多行文本

浅析Python正则如何处理带换行符多行文本

作者:detayun

本文针对Python正则表达式无法匹配多行文本的问题,提供了完整解决方案,核心原因是正则点号.默认不匹配换行符,导致跨行提取失败,文章给出了两种主流方法,大家可以根据需要进行选择

一、前言

日常开发中使用Python re 正则时,几乎所有人都会踩同一个坑:单行字符串匹配一切正常,一旦文本包含换行符,正则直接匹配不到内容,re.findall 返回空列表。

典型场景:

  1. 提取Markdown中 json / python 包裹的多行代码块;
  2. 截取多行注释、分段日志、多行接口返回文本;
  3. 抓取HTML/富文本中跨多行的标签内容;
  4. 像前文需求一样,提取大段跨换行JSON字符串。

出现该问题的根源不是正则表达式写错,而是Python正则内置的匹配规则限制。本文完整讲解换行处理原理、两套通用解决方案、实战案例与避坑技巧。

二、核心底层规则:点号.默认不匹配换行

正则中最常用的万能匹配符 . 存在硬性限制:

默认规则:. 匹配除换行符 \n 之外的所有字符。

举个直观例子:

import re

text = """第一行
第二行
第三行"""

# 尝试用.+匹配全部内容,会失败
res = re.findall(r'.+', text)
print(res)
# 输出:['第一行', '第二行', '第三行']

.+ 只会匹配单行内容,遇到换行直接终止,无法一次性抓取跨多行的整块文本。

如果我们想匹配 json` 和 ```中间跨多行的内容,直接写 json(.+?)````必然返回空列表,这就是你之前代码失效的根本原因。

三、两种主流跨行匹配解决方案

方案1:添加标志位 re.S(re.DOTALL)【推荐】

re.S 全称 DOTALL,作用:让 . 可以匹配换行符 \n,开启后点号真正代表「任意所有字符」,跨行匹配一键生效。

语法:

re.findall(正则, 文本, flags=re.S)

实战修复你之前提取JSON代码块的案例:

import re
str1 = '''
##随便一段前置文字
[
  {"content": "第一篇 战略管理", "level": 0},
  {"content": "1 目的", "level": 2}
]
##后置备注文字
'''
增加 flags=re.S 支持跨行匹配
res = re.findall(r'json(.+?)', str1, flags=re.S)
if res:
json_content = res[0].strip() # 清除首尾多余换行空格
print(“提取到多行JSON:\n”, json_content)

执行后可以完整取出中间所有换行的JSON文本,完美解决提取空白问题。

补充别名:`re.DOTALL` 和 `re.S` 完全等价,二者随便使用。

方案2:不使用标志位,替换万能匹配符 `[\s\S]`

如果项目规范不允许添加flag、或者部分环境无法传参,可以用 `[\s\S]` 替代 `.`。

二者组合 `[\s\S]` = 全部字符,天然支持换行,不需要额外标志位。

改造上例正则:

# [\s\S]+? 替代 .+?
res = re.findall(r'```json([\s\S]+?)```', str1)

效果和 re.S 完全一致,适合不方便修改flag参数的场景。

四、补充:容易混淆的 re.M 多行模式(和换行匹配无关)

很多新手会混淆 re.Mre.S,这里明确区分:

  1. re.S:控制 . 是否匹配换行,用于跨多行抓取整块内容
  2. re.M(MULTILINE):仅修改 ^$ 的匹配规则,^ 匹配每行开头,$ 匹配每行结尾,不改变点号 . 的行为,无法解决跨行提取失败问题。

错误示范(很多人踩坑):

# 只加re.M,不加re.S,依旧匹配不到跨多行内容
res = re.findall(r'```json(.+?)```', str1, re.M)
print(res)  # [] 空列表

如果需要同时开启两种模式,使用 | 分隔:

re.findall(pattern, text, re.S | re.M)

五、常用实战案例

案例1:提取多行Python代码块

import re
md_text = """
##测试文本
def test():
    print("多行函数")
    return 123
"""
pattern = r'python([\s\S]+?)'
code = re.findall(pattern, md_text)[0].strip()
print(code)

案例2:匹配跨多行HTML标签内容

html = """
<div class="box">
    第一行文字
    第二行文字
</div>
"""
res = re.findall(r'<div class="box">(.+?)</div>', html, re.S)
print(res[0].strip())

案例3:逐行匹配(不需要跨行,仅遍历每行)

如果需求不是抓取整块多行内容,而是单独匹配每一行,无需开启 re.S,直接循环结果即可:

log = """2026-01-01 日志1
2026-01-02 日志2
2026-01-03 日志3"""
# 匹配所有数字开头日志行
lines = re.findall(r'^\d{4}-\d{2}-\d{2}.*', log, re.M)
print(lines)

六、开发避坑优化技巧

1.正则字符串统一使用原始字符串 r""

正则表达式前缀加 r,避免 \n\s 等转义符被Python提前解析,防止匹配异常。

2.提取后使用strip()清理多余换行空格

跨行匹配出来的内容首尾通常自带换行、空白,调用 .strip() 去除冗余空白字符。

3.非贪婪匹配 +? 避免匹配范围过大

使用 (.+?) / ([\s\S]+?) 非贪婪模式,防止文本中存在多个 ```时,一次性匹配到最后一个结束标记。

4.判断匹配结果非空再取值

匹配失败返回空列表,直接取 res[0] 会报索引错误,增加判断:

res = re.findall(r'```json(.+?)```', str1, re.S)
if res:
    content = res[0].strip()
else:
    print("未找到对应代码块")

七、总结

  1. Python正则 . 默认不能匹配换行符,这是多行文本提取空白的核心原因;
  2. 主流解决方案:添加 flags=re.S. 支持换行,或使用 [\s\S] 替代 .
  3. re.M 仅作用于行首尾符 ^/$,不能解决跨行抓取问题,不要混用混淆;
  4. 处理Markdown代码块、多行标签、分段日志等场景,优先使用 re.S 方案,代码简洁易维护;
  5. 规范使用原始字符串、非贪婪匹配、空值判断,减少线上正则报错。

掌握以上规则后,所有带换行符的文本正则匹配问题都可以一次性解决。

到此这篇关于浅析Python正则如何处理带换行符多行文本的文章就介绍到这了,更多相关Python正则处理带换行符多行文本内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文