gpt4 book ai didi

Python 在支持反向引用的同时替换多个字符串

转载 作者:太空狗 更新时间:2023-10-30 02:39:09 24 4
gpt4 key购买 nike

some nice ways处理 python 中的同时多字符串替换。但是,我在创建一个既能做到这一点又能支持反向引用的高效函数时遇到了麻烦。

我想要的是使用表达式/替换术语的字典,其中替换术语可能包含对与表达式匹配的内容的反向引用

例如(注意\1)

repdict = {'&&':'and', '||':'or', '!([a-zA-Z_])':'not \1'}

我将一开始提到的 SO 答案放入下面的函数中,它适用于不包含反向引用的表达式/替换对:

def replaceAll(repdict, text):
repdict = dict((re.escape(k), v) for k, v in repdict.items())
pattern = re.compile("|".join(repdict.keys()))
return pattern.sub(lambda m: repdict[re.escape(m.group(0))], text)

但是,它不适用于包含反向引用的键..

>>> replaceAll(repldict, "!newData.exists() || newData.val().length == 1")
'!newData.exists() or newData.val().length == 1'

如果我手动执行,它工作正常。例如:

pattern = re.compile("!([a-zA-Z_])")
pattern.sub(r'not \1', '!newData.exists()')

按预期工作:

'not newData.exists()'

在花哨的函数中,转义似乎弄乱了使用 backref 的键,所以它永远不会匹配任何东西。

我最终想到了这个。但是,请注意,输入参数中支持backrefs的问题并没有解决,我只是在replacer函数中手动处理:

def replaceAll(repPat, text):
def replacer(obj):
match = obj.group(0)
# manually deal with exclamation mark match..
if match[:1] == "!": return 'not ' + match[1:]
# here we naively escape the matched pattern into
# the format of our dictionary key
else: return repPat[naive_escaper(match)]

pattern = re.compile("|".join(repPat.keys()))
return pattern.sub(replacer, text)

def naive_escaper(string):
if '=' in string: return string.replace('=', '\=')
elif '|' in string: return string.replace('|', '\|')
else: return string

# manually escaping \ and = works fine
repPat = {'!([a-zA-Z_])':'', '&&':'and', '\|\|':'or', '\=\=\=':'=='}
replaceAll(repPat, "(!this && !that) || !this && foo === bar")

返回:

'(not this and not that) or not this'

因此,如果有人知道如何制作支持反向引用并接受替换项作为输入的多字符串替换函数,我将非常感谢您的反馈。

最佳答案

更新:参见 Angus Hollands' answer寻找更好的选择。


我想不出比坚持将所有字典键组合成一个巨大的正则表达式的最初想法更简单的方法。

但是,也有一些困难。让我们假设一个像这样的 repldict:

repldict = {r'(a)': r'\1a', r'(b)': r'\1b'}

如果我们将它们组合成一个正则表达式,我们会得到 (a)|(b) - 所以现在 (b) 不再是第 1 组,这意味着它反向引用将无法正常工作。

另一个问题是我们无法判断要使用哪个替换项。如果正则表达式匹配文本 b,我们如何确定 \1b 是合适的替换?这是不可能的;我们没有足够的信息。

这些问题的解决方案是将每个字典键包含在一个命名组中,如下所示:

(?P<group1>(a))|(?P<group2>(b))

现在我们可以轻松识别匹配的键,并重新计算反向引用以使它们相对于该组。所以 \1b 指的是“group2 之后的第一组”。


实现如下:

def replaceAll(repldict, text):
# split the dict into two lists because we need the order to be reliable
keys, repls = zip(*repldict.items())

# generate a regex pattern from the keys, putting each key in a named group
# so that we can find out which one of them matched.
# groups are named "_<idx>" where <idx> is the index of the corresponding
# replacement text in the list above
pattern = '|'.join('(?P<_{}>{})'.format(i, k) for i, k in enumerate(keys))

def repl(match):
# find out which key matched. We know that exactly one of the keys has
# matched, so it's the only named group with a value other than None.
group_name = next(name for name, value in match.groupdict().items()
if value is not None)
group_index = int(group_name[1:])

# now that we know which group matched, we can retrieve the
# corresponding replacement text
repl_text = repls[group_index]

# now we'll manually search for backreferences in the
# replacement text and substitute them
def repl_backreference(m):
reference_index = int(m.group(1))

# return the corresponding group's value from the original match
# +1 because regex starts counting at 1
return match.group(group_index + reference_index + 1)

return re.sub(r'\\(\d+)', repl_backreference, repl_text)

return re.sub(pattern, repl, text)

测试:

repldict = {'&&':'and', r'\|\|':'or', r'!([a-zA-Z_])':r'not \1'}
print( replaceAll(repldict, "!newData.exists() || newData.val().length == 1") )

repldict = {'!([a-zA-Z_])':r'not \1', '&&':'and', r'\|\|':'or', r'\=\=\=':'=='}
print( replaceAll(repldict, "(!this && !that) || !this && foo === bar") )

# output: not newData.exists() or newData.val().length == 1
# (not this and not that) or not this and foo == bar

注意事项:

  • 仅支持数字反向引用;没有命名引用。
  • 默默地接受无效的反向引用,如 {r'(a)': r'\2'}。 (这些将有时抛出错误,但并非总是如此。)

关于Python 在支持反向引用的同时替换多个字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45128959/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com