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']