Python正则表达式删除以{{source|开头的嵌套双大括号内容
适配嵌套结构的Wiktionary source模板删除方案
原有正则失效的核心原因
你之前编写的re.sub("{{source\|.*?}}", "", line)无法正常工作,根源在于非贪婪匹配.*?}}的匹配逻辑:
- 它会在遇到**第一个出现的
}}**时就停止匹配 - 碰到样例中
{{source|内部嵌套了{{nom w pc|Chantal|Bouchard}}这类子模板时,会提前在内层模板的结束位置截断匹配,仅删除了{{source|{{nom w pc|Chantal|Bouchard}}这一小段,剩下的引用文本和外层残留的}}完全没有被覆盖,自然达不到预期的删除效果。
可行解决方案
以下两种方案都可以同时覆盖有嵌套、无嵌套的{{source|...}}块的删除需求:
方案1:多层适配的正则匹配
针对Wiktionary模板的常见嵌套深度(最多2层双大括号嵌套),可以使用适配嵌套结构的正则,无需逐字符遍历即可完成替换:
import re line = "# Test is a cool word {{source|{{nom w pc|Chantal|Bouchard}}, ''La langue et le nombril'', Presses de l'Université de Montréal (PUM), 2020, p. 174}}." # 正则逻辑:匹配以{{source|开头,内部可包含一层双大括号嵌套,直到匹配到最外层的结束}} pattern = r"\{\{source\|(?:[^{}]|\{\{[^{}]*\}\})*\}\}" processed_line = re.sub(pattern, "", line) print(processed_line) # 初步输出:# Test is a cool word .
该正则同样可以处理无嵌套的简单source块,比如输入# Test is a cool word {{source|Nicolas|Daniel, Presses de l'Université de Montréal 4}},处理后会直接删除整个source块,仅保留前面的文本内容。
方案2:逐字符深度追踪(适配任意层数嵌套)
如果碰到嵌套层数不确定、结构复杂的模板,逐字符遍历的方法不会出现匹配偏差,可靠性更高:
def remove_wiktionary_source(text): result = [] index = 0 text_length = len(text) while index < text_length: # 检测到 {{source| 模板起始位置 if text[index:index+9] == "{{source|": brace_depth = 1 index += 9 # 逐字符扫描,追踪双大括号的嵌套深度 while index < text_length and brace_depth > 0: if text[index:index+2] == "{{": brace_depth += 1 index += 2 elif text[index:index+2] == "}}": brace_depth -= 1 index += 2 else: index += 1 # 整个source块扫描完成,跳过块内所有内容,不加入最终结果 else: result.append(text[index]) index += 1 return "".join(result) # 测试用例 line1 = "# Test is a cool word {{source|{{nom w pc|Chantal|Bouchard}}, ''La langue et le nombril'', Presses de l'Université de Montréal (PUM), 2020, p. 174}}." line2 = "# Test is a cool word {{source|Nicolas|Daniel, Presses de l'Université de Montréal 4}}" print(remove_wiktionary_source(line1)) # 初步输出:# Test is a cool word . print(remove_wiktionary_source(line2)) # 输出:# Test is a cool word
小提示:替换完成后如果出现单词和后续标点之间的多余空格(比如原句结构为「word 空格 {{source|...}} 标点」,删除模板后会留下「word 空格 标点」的结构),可以通过简单的空格清理处理,比如执行
processed_line = re.sub(r'\s+([.,!?;:])', r'\1', processed_line),把标点前的多余空格去掉,得到和预期完全一致的输出# Test is a cool word.。
内容的提问来源于stack exchange,提问作者kavaliero
相关产品推荐
相关产品推荐

