Python 评估一个字符串(正则表达式有效吗?)
Python evaluate a string (Would Regular Expression work?)
基本上我想要完成的是,只要方括号内的数字位于字符串之前,方括号内的字符串就应该重复多次。我正在学习正则表达式,我发布了一个问题,我如何才能为这种情况想出一个正则表达式,人们很友好地让我得到了我想要的东西。基本上,下面的代码根据正则表达式解析字符串,并为我提供一个删除了所有方括号的列表,以便我可以遍历列表并获得我想要的输出。
给定一个字符串 s,我的代码如下。但这并不适用于所有情况。然而,这在没有嵌套方括号时有效。
import re
s = "abc3[cd]xyz"
s_final = ""
res = re.findall(r'\d+|[^\W\d_]+', s)
print(res)
for i in range(len(res)):
if res[i].isnumeric():
s_final+=int(res[i])*res[i+1]
else:
if res[i] not in s_final:
s_final += res[i]
print(s_final)
如果我将字符串更改为
s = "3[a2[c]]" 预期的输出是 accaccacc 因为首先评估内部方括号,然后评估外部方括号。但我得到的是 aaacc 根据我的代码,这并没有错。但是有人对我如何正确评估字符串有任何见解吗?
谢谢!
您可以使用
import re
s = "3[a2[c]]"
s_final = s
rx = r'(\d+)\[([^][]*)]'
n = 1
while n:
s_final, n = re.subn(rx, lambda x: x.group(2) * int(x.group(1)), s_final)
print(s_final) # => accaccacc
参见Python demo。
(\d+)\[([^][]*)]
正则表达式匹配任何一个或多个数字并将其捕获到组 1 中,然后匹配 [
字符,将 [
和 [ 以外的任何文本捕获到组 2 中=14=] 个字符到最接近的 ]
个字符。
n = 1
是使 re.subn
运行 至少一次的标志。
然后,执行 while
块以查找所有出现的模式并替换为第 1 组重复第 2 次的值。
首先解析字符后,您可以使用递归构建完整的字符串:
import re
s = "3[a2[c]]"
def build_string(d):
while (n:=next(d, None)) is not None and n != ']':
yield n if not (j:=re.findall('\d+$', n)) else n[:-1*len(j[0])]
if j:
_ = next(d)
yield ''.join(build_string(d))*int(j[0])
def get_result(s):
return ''.join(build_string(iter(re.findall('\w+|\[|\]', s))))
print(get_result(s))
输出:
'accaccacc'
无论使用什么解析工具,我总是建议开发人员首先为他们的解析器编写一个 BNF (Backus-Naur Form),作为后续开发的路线图。这是我从您的问题描述中收集到的内容(其中“+”表示“一个或多个”):
string_expr := (letter | repetition)+
repetition := integer '[' string_expr ']'
letter := character a-z
integer := (character 0-9)+
此时,使用您的测试样本遍历 BNF,并确保一切看起来正确。
你可以看到这是一个递归语法,因为string_expr
在它的定义中使用了repetition
,但是repetition
使用了string_expr
.
编写 Pyparsing 是为了方便将这些 BNF 定义映射到 pyparsing 对象。按照这个带注释的代码:(需要自下而上地实现你的语法)
import pyparsing as pp
# define []'s, but we'll use pyparsing's Suppress since they won't be
# needed in the actual expression after parsing is done
LBRACK, RBRACK = map(pp.Suppress, "[]")
# a letter will be a single character from the list of lowercase letters
# defined in the Python string module
letter = pp.Char(string.ascii_lowercase)
# an integer will be one or more numeric digits - attach a parse
# action to convert parsed numeric strings into actual Python ints
integer = pp.Word("0123456789").addParseAction(lambda t: int(t[0]))
# we would like to write 'repetition' now, but we haven't defined
# 'string_expr' yet. We can't define that yet because 'repetition' isn't
# defined yet. So let's define 'string_expr' using a Pyparsing
# placeholder - a Forward
string_expr = pp.Forward()
repetition = (integer("multiplier")
+ LBRACK
+ string_expr("content")
+ RBRACK)
# now that repetition is defined, we can "insert" the definition of
# 'string_expr' into the placeholder we created earlier
string_expr <<= (letter | repetition)[...]
# two more parse actions needed:
# - one to do the multiplication of multiplier*content in repetition
# - one to join all the pieces together in string_expr
repetition.addParseAction(lambda t: t.multiplier * list(t.content))
string_expr.addParseAction("".join)
现在使用 pyparsing 在解析器元素上定义的 runTests() 方法对其进行测试:
string_expr.runTests("""\
abc3[cd]xyz
3[a2[c]]
3[a2[cb]]
""")
给出:
abc3[cd]xyz
['abccdcdcdxyz']
3[a2[c]]
['accaccacc']
3[a2[cb]]
['acbcbacbcbacbcb']
基本上我想要完成的是,只要方括号内的数字位于字符串之前,方括号内的字符串就应该重复多次。我正在学习正则表达式,我发布了一个问题,我如何才能为这种情况想出一个正则表达式,人们很友好地让我得到了我想要的东西。基本上,下面的代码根据正则表达式解析字符串,并为我提供一个删除了所有方括号的列表,以便我可以遍历列表并获得我想要的输出。
给定一个字符串 s,我的代码如下。但这并不适用于所有情况。然而,这在没有嵌套方括号时有效。
import re
s = "abc3[cd]xyz"
s_final = ""
res = re.findall(r'\d+|[^\W\d_]+', s)
print(res)
for i in range(len(res)):
if res[i].isnumeric():
s_final+=int(res[i])*res[i+1]
else:
if res[i] not in s_final:
s_final += res[i]
print(s_final)
如果我将字符串更改为
s = "3[a2[c]]" 预期的输出是 accaccacc 因为首先评估内部方括号,然后评估外部方括号。但我得到的是 aaacc 根据我的代码,这并没有错。但是有人对我如何正确评估字符串有任何见解吗?
谢谢!
您可以使用
import re
s = "3[a2[c]]"
s_final = s
rx = r'(\d+)\[([^][]*)]'
n = 1
while n:
s_final, n = re.subn(rx, lambda x: x.group(2) * int(x.group(1)), s_final)
print(s_final) # => accaccacc
参见Python demo。
(\d+)\[([^][]*)]
正则表达式匹配任何一个或多个数字并将其捕获到组 1 中,然后匹配 [
字符,将 [
和 [ 以外的任何文本捕获到组 2 中=14=] 个字符到最接近的 ]
个字符。
n = 1
是使 re.subn
运行 至少一次的标志。
然后,执行 while
块以查找所有出现的模式并替换为第 1 组重复第 2 次的值。
首先解析字符后,您可以使用递归构建完整的字符串:
import re
s = "3[a2[c]]"
def build_string(d):
while (n:=next(d, None)) is not None and n != ']':
yield n if not (j:=re.findall('\d+$', n)) else n[:-1*len(j[0])]
if j:
_ = next(d)
yield ''.join(build_string(d))*int(j[0])
def get_result(s):
return ''.join(build_string(iter(re.findall('\w+|\[|\]', s))))
print(get_result(s))
输出:
'accaccacc'
无论使用什么解析工具,我总是建议开发人员首先为他们的解析器编写一个 BNF (Backus-Naur Form),作为后续开发的路线图。这是我从您的问题描述中收集到的内容(其中“+”表示“一个或多个”):
string_expr := (letter | repetition)+
repetition := integer '[' string_expr ']'
letter := character a-z
integer := (character 0-9)+
此时,使用您的测试样本遍历 BNF,并确保一切看起来正确。
你可以看到这是一个递归语法,因为string_expr
在它的定义中使用了repetition
,但是repetition
使用了string_expr
.
编写 Pyparsing 是为了方便将这些 BNF 定义映射到 pyparsing 对象。按照这个带注释的代码:(需要自下而上地实现你的语法)
import pyparsing as pp
# define []'s, but we'll use pyparsing's Suppress since they won't be
# needed in the actual expression after parsing is done
LBRACK, RBRACK = map(pp.Suppress, "[]")
# a letter will be a single character from the list of lowercase letters
# defined in the Python string module
letter = pp.Char(string.ascii_lowercase)
# an integer will be one or more numeric digits - attach a parse
# action to convert parsed numeric strings into actual Python ints
integer = pp.Word("0123456789").addParseAction(lambda t: int(t[0]))
# we would like to write 'repetition' now, but we haven't defined
# 'string_expr' yet. We can't define that yet because 'repetition' isn't
# defined yet. So let's define 'string_expr' using a Pyparsing
# placeholder - a Forward
string_expr = pp.Forward()
repetition = (integer("multiplier")
+ LBRACK
+ string_expr("content")
+ RBRACK)
# now that repetition is defined, we can "insert" the definition of
# 'string_expr' into the placeholder we created earlier
string_expr <<= (letter | repetition)[...]
# two more parse actions needed:
# - one to do the multiplication of multiplier*content in repetition
# - one to join all the pieces together in string_expr
repetition.addParseAction(lambda t: t.multiplier * list(t.content))
string_expr.addParseAction("".join)
现在使用 pyparsing 在解析器元素上定义的 runTests() 方法对其进行测试:
string_expr.runTests("""\
abc3[cd]xyz
3[a2[c]]
3[a2[cb]]
""")
给出:
abc3[cd]xyz
['abccdcdcdxyz']
3[a2[c]]
['accaccacc']
3[a2[cb]]
['acbcbacbcbacbcb']