
本文介绍一种基于正则表达式与状态跟踪的 python 方法,用于自动修复第三方生成的 markdown 文档中混乱的列表缩进(如不一致空格、多余换行、嵌套层级错位),使其符合 commonmark 标准并兼容主流解析器(如 python-markdown、mistune)。
本文介绍一种基于正则表达式与状态跟踪的 python 方法,用于自动修复第三方生成的 markdown 文档中混乱的列表缩进(如不一致空格、多余换行、嵌套层级错位),使其符合 commonmark 标准并兼容主流解析器(如 python-markdown、mistune)。
在自动化文档生成流程中,常需处理由非标准工具输出的 Markdown 原文——这类内容虽语义正确,但缩进不统一、空行冗余、嵌套层级错乱,极易导致 HTML 渲染异常(例如子项脱离父列表、格式塌陷或解析失败)。核心挑战在于:Markdown 列表的嵌套层级并非由绝对缩进值决定,而是依赖于同一列表内首个缩进项所确立的“缩进单位”(如 2 空格或 4 空格),后续项需按该单位的整数倍对齐;而混用无序(*, -, +)与有序(1., 2.)列表时,还需独立维护类型状态。
以下方案采用单次正则遍历 + 全局状态跟踪,高效完成三重净化:
- 压缩冗余空行:将 ≥3 个连续换行符统一替换为两个(保留段落分隔,消除列表中断);
-
标准化列表符号:统一使用
-表示无序项(兼容性最佳),保留原始数字序号; -
动态重算嵌套层级:以列表中首个缩进项的空格数为基准单位,后续项缩进按
floor(实际空格数 / 单位) + 修正计算,并强制使用 4 空格/级的标准缩进。
import re
def sanitize_markdown_lists(md_text: str) -> str:
# 步骤1:压缩多余空行(≥3 → 2)
md_text = re.sub(r'\n{3,}', '\n\n', md_text.strip())
# 步骤2:匹配所有列表项(支持无序/有序,捕获缩进、标记、内容)
list_pattern = re.compile(
r'(^|\n+)(\s*)(([-*+])|(\d+\.\s*))(\s+)(.*)$',
re.MULTILINE
)
# 状态变量(闭包模拟,避免全局污染)
state = {
'prev_level': 0, # 上一项的嵌套层级(0=顶层)
'multiplicity': 0, # 当前列表的缩进单位(如4空格/级)
'prev_state': 0 # 0=未开始, 1=无序列表, 2=有序列表
}
def fix_item(match):
nonlocal state
prefix = match.group(1) or '\n' # 行首或换行符
indent_ws = match.group(2) or ''
bullet = match.group(4)
enum_num = match.group(5)
spacer = match.group(6) or ''
content = match.group(7) or ''
# 判定当前列表类型
current_state = 1 if bullet else 2
# 计算当前层级:仅当有缩进时才计算
current_level = 0
if indent_ws:
if state['prev_level'] == 0:
# 首个缩进项:确立缩进单位
state['multiplicity'] = len(indent_ws)
current_level = 1
else:
# 按单位换算层级(向上取整处理错位)
unit = state['multiplicity']
current_level = (len(indent_ws) + unit - 1) // unit
# 构建标准化输出
result = prefix if state['prev_state'] == current_state else '\n'
result += ' ' * (current_level * 4) # 强制4空格/级
result += '-' if bullet else (enum_num or '')
result += spacer
result += content
# 更新状态
state['prev_level'] = current_level
state['prev_state'] = current_state
return result
# 执行替换
return list_pattern.sub(fix_item, md_text)
# 使用示例
raw_md = """## Enumeration
1. Element 1
2. Element 2
## List (all elements rely on the same topic)
* Element 1 level 1
* **Element 2** Level 2
* Element 3 level 1
* Element 4 level 2
* Element 5 level 3
- Element 6 level 1
- Element 7 level 2
- Element 8 level 3"""
clean_md = sanitize_markdown_lists(raw_md)
print(clean_md)✅ 关键优势:
-
无需外部依赖:纯 Python
re模块实现,轻量可靠; - 状态感知:准确区分不同列表上下文,避免跨列表层级污染;
-
容错修正:对
2或6空格等非整除缩进自动向上取整,防止子项“消失”; -
渲染友好:输出严格遵循 CommonMark 规范,可无缝接入
markdown-it-py、mistune或Python-Markdown。
⚠️ 注意事项:
- 本方案假设列表项间无复杂内联结构干扰(如代码块、HTML 标签)。若存在,建议先用
markdown-it-py的parse()获取 AST 进行结构化清洗; - 对含
>引用块或:::脚注的混合文档,需前置剥离再处理; - 生产环境建议添加单元测试,覆盖边界用例(如单行列表、空内容、超深嵌套)。
经此处理,原始混乱列表将转化为语义清晰、缩进统一、渲染稳定的 Markdown,显著提升自动化文档管线的鲁棒性与可维护性。


















