You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python正则替换时如何排除指定分隔块内的文本?

如何在正则替换时排除指定文本块内容

需求描述

处理长文本时,存在以----为分隔符的特殊块(支持空块),要求对文本整体执行正则替换,但完全跳过特殊块内的内容。

举个替换text为TEXT的示例:
输入:

some text
----
text inside special block
----
some text
----
----
some text
----
text inside special block
----
some text

预期输出:

some TEXT
----
text inside special block
----
some TEXT
----
----
some TEXT
----
text inside special block
----
some TEXT

当前实现是先解析文本获取特殊块的行索引,再逐行处理替换、跳过特殊块行,但这种方案在处理跨行正则时会变得异常复杂。因此需要更优方案:直接使用re.sub处理全文本时,排除指定字符区间(span)内的内容,即使正则匹配与区间相交,也确保区间内内容不被替换。

当前实现代码

import re

def find_code_lines(data):
    # 匹配特殊块的正则(支持空块)
    r = re.compile(r'(\n----(?=\n)(?P<group1>[\s\S]*?\n)----\n)')
    
    # 移除转义的\n(非实际换行的情况)
    data_edited = data.replace('\\n', '')

    # 保存特殊块的字符索引区间
    char_spans = []
    for m in r.finditer(data_edited):
        char_spans.append(m.span(1))
    
    # 转换为行索引区间
    line_spans = []
    for span in char_spans:
        begin = data_edited[:span[0]].count("\n") + 2
        end = data_edited[:span[1]].count("\n") - 1
        line_spans.append((begin, end))
        
    return line_spans

# 判断行索引是否在特殊块内
def in_spans(spans, line_index):
    res = False
    for span in spans:
        if line_index >= span[0] and line_index < span[1]:
            res = True
    return res

# 逐行处理替换
def process_text(data):
    code_lines = find_code_lines(data)
    lines_edited = []
    data_lines = data.splitlines()
    replace_count = 0

    for i in range(len(data_lines)):
        if in_spans(code_lines, i):
            lines_edited.append(data_lines[i])
        else:
            # 替换$...$为stem:[...]
            data_tuple = re.subn(r'(?P<group1>\s|^|\s\()\$(?P<group2>[^\$`\r\n]{1,1000}?)\$',
                                r'\1stem:[\2]', 
                                data_lines[i])
            if data_tuple[1] == 0:
                lines_edited.append(data_lines[i])
            else:
                lines_edited.append(data_tuple[0])
                replace_count += data_tuple[1]

    lines_edited.append('')
    data = '\n'.join(lines_edited)
    print(f'Replaced Math blocks: {replace_count}')
    return data

更优解决方案:正则分支优先匹配特殊块

核心思路是构造一个正则模式,优先匹配完整的特殊块,再匹配需要替换的目标内容。通过re.sub的回调函数,直接返回特殊块的原内容,仅对非特殊块的内容执行替换逻辑。

通用实现框架

import re

def replace_outside_blocks(text, block_pattern, target_pattern, replace_func):
    # 组合正则:先匹配特殊块,再匹配目标内容
    combined_pattern = re.compile(f'({block_pattern})|({target_pattern})')
    
    def replacer(match):
        if match.group(1):
            # 匹配到特殊块,直接返回原内容
            return match.group(1)
        elif match.group(2):
            # 匹配到目标内容,执行替换
            return replace_func(match)
        return match.group(0)
    
    return combined_pattern.sub(replacer, text)

适配你的数学公式替换需求

针对你需要替换$...$为stem:[...]的场景,具体实现如下:

import re

def replace_math_outside_blocks(data):
    # 预处理:移除转义的\n(非实际换行)
    data_edited = data.replace('\\n', '')
    
    # 匹配----分隔块的正则,处理开头/结尾的边界情况
    block_pattern = r'^----\n[\s\S]*?\n----$|(?<=\n)----\n[\s\S]*?\n----(?=\n)'
    # 数学公式匹配模式
    math_pattern = r'(?P<group1>\s|^|\s\()\$(?P<group2>[^\$`\r\n]{1,1000}?)\$'
    
    replace_count = 0
    
    def replace_math(match):
        nonlocal replace_count
        replace_count += 1
        return f'{match.group("group1")}stem:[{match.group("group2")}]'
    
    processed_data = replace_outside_blocks(data_edited, block_pattern, math_pattern, replace_math)
    print(f'Replaced Math blocks: {replace_count}')
    return processed_data

# 使用示例
# data = 你的输入文本
# result = replace_math_outside_blocks(data)

方案优势

  1. 支持跨行正则:无需拆分文本为行,直接处理全文,完美适配跨行匹配需求
  2. 逻辑简洁:通过正则分支优先匹配特殊块,天然排除块内内容,无需手动管理区间
  3. 易维护:替换逻辑集中在回调函数中,修改或扩展替换规则只需调整回调即可

内容的提问来源于stack exchange,提问作者Alexey Bogdanov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 01:50:02