首页 > 解决方案 > python中的嵌套替换

问题描述

我需要过滤一个(长)文本文件替换模式,可能是嵌套的。

不幸的是,模式有些模棱两可(以下不是完整列表):

嵌套模式时会出现问题,例如:

\tqt{Da oggi sarai conosciuto anche come \textbf{"guds morder"}: uccisore di Dei}, furono le ultime parole che sentì.

一个天真的实现:

import re
line = 'tqt{Da oggi sarai conosciuto anche come \textbf{"guds morder"}: uccisore di Dei}, furono le ultime parole che sentì.'
line = re.sub(r'\\textbf{([^}]+)}', r'@b{\1}b@', line)
line = re.sub(r'\\tqt{([^}]+)}', r'@q{\1}q@', line)

产生错误答案 ( @q{Da oggi sarai conosciuto anche come @b{"guds morder"}q@b@: uccisore di Dei}, furono le ultime parole che sentì.),因为中间形式 ( \\tgt{Da oggi sarai conosciuto anche come @b{``guds morder''}b@: uccisore di Dei}, furono le ultime parole che sentì.) 有点模棱两可,并且以下模式匹配“错误”右括号(“正确”字符串应该是:)@q{Da oggi sarai conosciuto anche come @b{"guds morder"}b@: uccisore di Dei}q@, furono le ultime parole che sentì.

我曾想过分两步执行此操作,使用一些中间(明确)形式,但这过于复杂,并且在模式顺序颠倒的情况下无济于事(文件很长并且存在多个嵌套条件)。

注意:嵌套总是完整的;IE; 模式永远不会跨越彼此的界限,否则问题将无法解决。

进行此类替换的pythonic方法是什么?

标签: pythonstringreplacenested

解决方案


Pyparsing应该适合这项工作。您可以使用Forward为您的“随便”进行递归定义。

这是一个示例,带有一些调试打印以了解发生了什么:

import pyparsing as pp
pp.ParserElement.setDefaultWhitespaceChars('') #want to preserve whitespace as is

#a placeholder, to be filled in later
whatever = pp.Forward()

textit = "\\textit{" + whatever + "}"
def textit_action(inputs):
    print('textit')
    outputs = ["@e{"+''.join(inputs[1:-1])+"}e@"]
    return outputs
textit.setParseAction(textit_action)

textbf = "\\textbf{" + whatever + "}"
def textbf_action(inputs):
    print('textbf')
    outputs = ["@b{"+''.join(inputs[1:-1])+"}b@"]
    return outputs
textbf.setParseAction(textbf_action)

tqt = "\\tqt{" + whatever + "}"
def tqt_action(inputs):
    print('tqt')
    print(inputs)
    outputs = ["@q{"+''.join(inputs[1:-1])+"}q@"]
    return outputs
tqt.setParseAction(tqt_action)

anything = pp.Regex('[^\}\{]') 
#(there is probably a more pyparsing-y way to do this)
#Matching only a single character to make this not greedy.
#Else it matches e.g. 'test \textbf', swallowing the textbf.
#This is prevented now, as or_ takes the first that matches.
def anything_action(inputs):
    print('anything')
    print(inputs)
    return inputs
anything.setParseAction(anything_action)

other_brackets = '{' + whatever + '}'
def other_brackets_action(inputs):
    print('other brackets')
    print(inputs)
    return inputs
other_brackets.setParseAction(other_brackets_action)

or_ = pp.MatchFirst([textit, textbf, tqt, other_brackets, anything] )
whatever << pp.ZeroOrMore(or_)

def whatever_action(inputs):
    print('final')
    print(inputs)
    outputs = [''.join(inputs)]
    print(outputs)
    return outputs
whatever.setParseAction(whatever_action)

whatever.parseString(r'\tqt{Da oggi sarai conosciuto anche come \textbf{"guds morder"}: uccisore di Dei}, furono le ultime parole che sentì.')
(['@q{Da oggi sarai conosciuto anche come @b{"guds morder"}b@: uccisore di Dei}q@, furono le ultime parole che sentì.'], {})

推荐阅读