Python正则如何处理带换行符的多行文本|解决匹配空白、提取失败问题

Python正则如何处理带换行符的多行文本|解决匹配空白、提取失败问题

一、前言

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

典型场景:

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

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

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

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

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

举个直观例子:

importre 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代码块的案例:

importre str1=''' 随便一段前置文字 ```json[{"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`:所有空白字符(空格、制表符、换行 `\n`) - `\S`:所有非空白字符 二者组合 `[\s\S]` = 全部字符,天然支持换行,不需要额外标志位。 改造上例正则: ```python # [\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代码块

importre md_text=""" 测试文本 ```pythondeftest():print("多行函数")return123

“”"
pattern = r’python([\s\S]+?)
code = re.findall(pattern, md_text)[0].strip()
print(code)

### 案例2:匹配跨多行HTML标签内容 ```python 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提前解析,防止匹配异常。
    错误写法:'```json(.+?)```'
    标准写法:r'```json(.+?)```'

  2. 提取后使用strip()清理多余换行空格
    跨行匹配出来的内容首尾通常自带换行、空白,调用.strip()去除冗余空白字符。

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

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

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

七、总结

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

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