使用正则表达式在Python中递归替换字符串

2 投票
3 回答
3439 浏览
提问于 2025-04-16 23:45

我试过不同的方法,但到现在为止都没有成功。所以我发了这个帖子。

先说一下背景:我正在尝试理解一个Makefile,这个文件可能会包含来自其他文件的变量。我已经成功地把Makefile里的所有变量和它的包含内容读进了一个Python字典里。但现在我发现每个值实际上都引用了字典里其他变量的值。我想做的是把字典里的所有值展开,变成不依赖于其他键/值对的文本。这肯定需要用到递归(我觉得),但我很想听听其他的建议。

需要注意的是,并不是所有的变量都有对应的值。如果没有值,就把这个键替换成一个空字符串。

接下来是一些代码来演示上面说的内容:

假设有一组键值对:

*A = -L${F} ${B} ${D},

*B = -L/myhome,

*F = /usr/lib

我想写一个Python脚本(可能会用到正则表达式),递归地把符合'${XXX}'格式的值替换成对应的键,直到没有更多符合这个格式的值为止(也就是说,所有的值都被展开了)。因为D没有对应的值,所以我希望A的值最终变成(例如):

*A = -L/usr/lib -L/myhome

提前感谢大家的帮助,任何建议都非常感谢。

3 个回答

0

像这样:

def unroll(stuff):

   # Code to unroll.
   # if something had been replaced:
      replaced = True
   # else
      replaced = False

   return stuff, replaced


def main():

   stuff, replaced = unroll(stuff)
   while replaced:
      stuff, replaced = unroll(stuff)

不过要小心无限替换循环哦!

2

通过一个辅助函数来进行递归展开,你可以使用 re.sub 来替换每个值中所有不重叠的匹配项:

import re
RE_VAL = re.compile(r'\${(.*?)}')

def expand (val, src):
  return RE_VAL.sub(lambda m: expand(src.get(m.group(1), ''), src), val)

def main ():
  vals = {
      'A': '-L${F} ${B} ${D}',
      'B': '-L/myhome',
      'D': '${E}',
      'E': '${G}',
      'F': '/usr/lib',
      'G': '-O',
  }

  for k,v in vals.iteritems():
    vals[k] = expand(v, vals)
  print vals
  # {'A': '-L/usr/lib -L/myhome -O', 'B': '-L/myhome', 'E': '-O', 'D': '-O', 'G': '-O', 'F': '/usr/lib'}
4

可以利用 re.subn 这个函数,它会返回替换的次数(这样你就知道什么时候可以停止了),而且它的 repl 参数可以接受一个函数,这样你就可以从变量字典中选择值。

import re

vs = { 
    'A' : '-L${F} ${B} ${D}',
    'B' : '-L/myhome',
    'F' : '/usr/lib',
}

while 1:
    treps = 0
    for k in vs:
        ns, nreps = re.subn(r'''\${(\w+)}''', lambda match: vs.get(match.group(1), ''), vs[k])
        if nreps: vs[k] = ns
        treps += nreps
    if not treps: break

print(vs)
# {'A': '-L/usr/lib -L/myhome ', 'B': '-L/myhome', 'F': '/usr/lib'}

要注意,上面的程序如果 A=${A},或者 A=${B} 而 B=${A} 的话,就永远不会结束。你没有说明在这种情况下应该怎么处理。

撰写回答