有选择地替换字符串中特定的嵌套定界符(括号),同时尊重嵌套
Selectively replace specific nested delimiters (brackets) in strings, while respecting nesting
我有很多字符串,我试图有选择地将 f[--whatever--]
的所有实例替换为 f.__getitem__(--whatever--, x=x)
。
这是留给我的最后一个选项,可以使用 eval
调用来修补一些旧的复杂代码,不幸的是,我一直坚持使用它。
替换 f[
很容易,但很难知道 ]
的实例是否与此模式或其他一些模式相关联
杂项模式,如列表 [--whatever--]
或索引 .loc[--whatever--].
在我的字符串中没有 ]
不属于完整 []
的孤立情况。
我最近的解决方案尝试使用正则表达式:
1) sub ([^f])[(.+?)] with openbracketclosebracket 保留不属于 f[] 的 []
2)剩下的[]
3) 使用 []
后退开括号和闭括号
问题是这不能处理很多嵌套情况,如下例所示。我正在寻找一个更全面的解决方案来确定给定的 ]
是否与 f[]
或其他一些结构相关联。有没有办法用 pyparsing
或其他模块做到这一点?
例子
f[r@ndom t3xt] + [some r@ndom t3xt] + [f[more r@ndom t3xt] / f[more t3xt]] + [f[f[more t3xt] + 3]]
应该变成
f.__getitem__(r@ndom t3xt, x=x) + [some r@ndom t3xt] + [f.__getitem__(more r@ndom t3xt, x=x) / f.__getitem__(more t3xt, x=x)] + [f.__getitem__(f.__getitem__(more t3xt) + 3)]
也许,
f\[([^]]*)\]
和 re.sub
以及
f.__getitem__(, x=x)
可能只是工作。
测试
import re
regex = r"f\[([^]]*)\]"
string = """
f[r@ndom t3xt] + [some r@ndom t3xt] + [f[more r@ndom t3xt] / f[more t3xt]]
f[] + [] + [f[] / f[]]
"""
subst = "f.__getitem__(, x=x)"
print(re.sub(regex, subst, string))
输出
f.__getitem__(, x=x) + [some r@ndom t3xt] + [f.__getitem__(, x=x) / f.__getitem__(, x=x)]
f.__getitem__(, x=x) + [] + [f.__getitem__(, x=x) / f.__getitem__(, x=x)]
如果您希望 simplify/modify/explore 表达式,regex101.com. If you'd like, you can also watch in this link 的右上面板已对其进行说明,它将如何匹配一些示例输入。
嵌套的 [] 使这成为一个重要的问题。 pyparsing 有一个名为 nestedExpr
的 "crutch" 表达式方法,可以轻松匹配 () 和 [] 等嵌套分隔符。 pyparsing 也有 transformString 方法,用于将解析后的数据转换为不同的形式。我们可以使用解析时回调(或 "parse action")重复转换任何嵌套的 f[zzz]
项,直到所有项都已转换:
import pyparsing as pp
fname = pp.Keyword('f')
index_expr = pp.nestedExpr('[', ']')
# nestedExpr will give a nested list by default, we just want the original raw text
f_expr = fname + pp.originalTextFor(index_expr)("index_expr")
# define a parse action to convert the f[aaa] format to f._getitem__(aaa, x=x)
def convert_to_getitem(t):
# get the contents of the index_expr, minus the leading and trailing []'s
index_expr = t.index_expr[1:-1]
# repeatedly call transform string to get further nested f[] expressions, until
# transformString stops returning a modified string
while True:
transformed = f_expr.transformString(index_expr)
if transformed == index_expr:
break
index_expr = transformed
# reformat to use getitem
return "f.__getitem__({}, x=x)".format(transformed)
# add the parse action to f_expr
f_expr.addParseAction(convert_to_getitem)
# use transformString to convert the input string with nested expressions
sample = "f[r@ndom t3xt] + [some r@ndom t3xt] + [f[more r@ndom t3xt] / f[more t3xt]] + [f[f[more t3xt] + 3]]"
print(f_expr.transformString(sample))
打印:
f.__getitem__(r@ndom t3xt, x=x) + [some r@ndom t3xt] + [f.__getitem__(more r@ndom t3xt, x=x) / f.__getitem__(more t3xt, x=x)] + [f.__getitem__(f.__getitem__(more t3xt, x=x) + 3, x=x)]
这还应该处理可能出现在带引号的字符串中的“[]”。
使用正则表达式的解决方案:
import re
string1 = "f[r@ndom t3xt] + [some r@ndom t3xt] + 3[f2[more r@ndom t3xt] / f[more t3xt]] + [f[f[more t3xt] + 3]]"
string3 = '''f[text([0,[1,2],3, x["text3"]])]'''
def get_repl(match):
if match.groups()[-1]:
# replace nested [ and ] with special characters
return match.groups()[-1].replace('[', '##1##').replace(']', '##2##')
else:
return '{}.__getitem__({}, x=x)'.format(*match.groups()[:-1])
def place_by_getitem(string):
pattern = '(?<!\w)(f)\[([^\[]+?)\]|(\[[^\[]+?\])'
while re.search(pattern, string):
string = re.sub(pattern, get_repl, string)
return string.replace('##1##', '[').replace('##2##', ']')
print(place_by_getitem(string1))
print(place_by_getitem(string3))
输出:
f.__getitem__(r@ndom t3xt, x=x) + [some r@ndom t3xt] + 3[f2.__getitem__(more r@ndom t3xt, x=x) / f.__getitem__(more t3xt, x=x)] + [f.__getitem__(f.__getitem__(more t3xt, x=x) + 3, x=x)]
f.__getitem__(text([0,[1,2],3, x.__getitem__("text3", x=x)]), x=x)
我有很多字符串,我试图有选择地将 f[--whatever--]
的所有实例替换为 f.__getitem__(--whatever--, x=x)
。
这是留给我的最后一个选项,可以使用 eval
调用来修补一些旧的复杂代码,不幸的是,我一直坚持使用它。
替换 f[
很容易,但很难知道 ]
的实例是否与此模式或其他一些模式相关联
杂项模式,如列表 [--whatever--]
或索引 .loc[--whatever--].
在我的字符串中没有 ]
不属于完整 []
的孤立情况。
我最近的解决方案尝试使用正则表达式: 1) sub ([^f])[(.+?)] with openbracketclosebracket 保留不属于 f[] 的 [] 2)剩下的[] 3) 使用 []
后退开括号和闭括号问题是这不能处理很多嵌套情况,如下例所示。我正在寻找一个更全面的解决方案来确定给定的 ]
是否与 f[]
或其他一些结构相关联。有没有办法用 pyparsing
或其他模块做到这一点?
例子
f[r@ndom t3xt] + [some r@ndom t3xt] + [f[more r@ndom t3xt] / f[more t3xt]] + [f[f[more t3xt] + 3]]
应该变成
f.__getitem__(r@ndom t3xt, x=x) + [some r@ndom t3xt] + [f.__getitem__(more r@ndom t3xt, x=x) / f.__getitem__(more t3xt, x=x)] + [f.__getitem__(f.__getitem__(more t3xt) + 3)]
也许,
f\[([^]]*)\]
和 re.sub
以及
f.__getitem__(, x=x)
可能只是工作。
测试
import re
regex = r"f\[([^]]*)\]"
string = """
f[r@ndom t3xt] + [some r@ndom t3xt] + [f[more r@ndom t3xt] / f[more t3xt]]
f[] + [] + [f[] / f[]]
"""
subst = "f.__getitem__(, x=x)"
print(re.sub(regex, subst, string))
输出
f.__getitem__(, x=x) + [some r@ndom t3xt] + [f.__getitem__(, x=x) / f.__getitem__(, x=x)]
f.__getitem__(, x=x) + [] + [f.__getitem__(, x=x) / f.__getitem__(, x=x)]
如果您希望 simplify/modify/explore 表达式,regex101.com. If you'd like, you can also watch in this link 的右上面板已对其进行说明,它将如何匹配一些示例输入。
嵌套的 [] 使这成为一个重要的问题。 pyparsing 有一个名为 nestedExpr
的 "crutch" 表达式方法,可以轻松匹配 () 和 [] 等嵌套分隔符。 pyparsing 也有 transformString 方法,用于将解析后的数据转换为不同的形式。我们可以使用解析时回调(或 "parse action")重复转换任何嵌套的 f[zzz]
项,直到所有项都已转换:
import pyparsing as pp
fname = pp.Keyword('f')
index_expr = pp.nestedExpr('[', ']')
# nestedExpr will give a nested list by default, we just want the original raw text
f_expr = fname + pp.originalTextFor(index_expr)("index_expr")
# define a parse action to convert the f[aaa] format to f._getitem__(aaa, x=x)
def convert_to_getitem(t):
# get the contents of the index_expr, minus the leading and trailing []'s
index_expr = t.index_expr[1:-1]
# repeatedly call transform string to get further nested f[] expressions, until
# transformString stops returning a modified string
while True:
transformed = f_expr.transformString(index_expr)
if transformed == index_expr:
break
index_expr = transformed
# reformat to use getitem
return "f.__getitem__({}, x=x)".format(transformed)
# add the parse action to f_expr
f_expr.addParseAction(convert_to_getitem)
# use transformString to convert the input string with nested expressions
sample = "f[r@ndom t3xt] + [some r@ndom t3xt] + [f[more r@ndom t3xt] / f[more t3xt]] + [f[f[more t3xt] + 3]]"
print(f_expr.transformString(sample))
打印:
f.__getitem__(r@ndom t3xt, x=x) + [some r@ndom t3xt] + [f.__getitem__(more r@ndom t3xt, x=x) / f.__getitem__(more t3xt, x=x)] + [f.__getitem__(f.__getitem__(more t3xt, x=x) + 3, x=x)]
这还应该处理可能出现在带引号的字符串中的“[]”。
使用正则表达式的解决方案:
import re
string1 = "f[r@ndom t3xt] + [some r@ndom t3xt] + 3[f2[more r@ndom t3xt] / f[more t3xt]] + [f[f[more t3xt] + 3]]"
string3 = '''f[text([0,[1,2],3, x["text3"]])]'''
def get_repl(match):
if match.groups()[-1]:
# replace nested [ and ] with special characters
return match.groups()[-1].replace('[', '##1##').replace(']', '##2##')
else:
return '{}.__getitem__({}, x=x)'.format(*match.groups()[:-1])
def place_by_getitem(string):
pattern = '(?<!\w)(f)\[([^\[]+?)\]|(\[[^\[]+?\])'
while re.search(pattern, string):
string = re.sub(pattern, get_repl, string)
return string.replace('##1##', '[').replace('##2##', ']')
print(place_by_getitem(string1))
print(place_by_getitem(string3))
输出:
f.__getitem__(r@ndom t3xt, x=x) + [some r@ndom t3xt] + 3[f2.__getitem__(more r@ndom t3xt, x=x) / f.__getitem__(more t3xt, x=x)] + [f.__getitem__(f.__getitem__(more t3xt, x=x) + 3, x=x)]
f.__getitem__(text([0,[1,2],3, x.__getitem__("text3", x=x)]), x=x)