【问题标题】:Regex sub() method seems to replace every single character by the replacement group正则表达式 sub() 方法似乎用替换组替换了每个字符
【发布时间】:2020-09-02 07:36:15
【问题描述】:

在以下字典中,用户可以将键作为变量来定义另一个值:

d = {'a_key': 'a_value', 'b_key': '[ a_key ]+1/[a_key]'}

我必须用相应的值替换这些引用,以获得所需的输出:

d = {'a_key': 'a_value', 'b_key': 'a_value+1/a_value'}

引用由方括号分隔,以防止不必要的替换(它比Replace key references by corresponding values in dict values 要求的简单 str 替换更安全)。因此,我使用正则表达式来执行替换:

from re import search, sub
d = {'a_key': 'a_value', 'b_key': '[ a_key ]+1/[a_key]'}
for k in d.keys():
    parameter = search(r"\[\s*(\w+)\s*\]", d[k])
    if parameter is not None and parameter.group(1) in d.keys():
        print("target: "+parameter.group())
        print("to be replaced by: "+d[parameter.group(1)])
        d[k] = sub(parameter.group(), d[parameter.group(1)], d[k])
print(d)

输出是:

target: [ a_key ]
to be replaced by: a_value
{'a_key': 'a_value', 'b_key': '[a_valuea_valuea_valuea_valuea_valuea_valuea_value]+1/[a_valuea_valuea_valuea_valuea_value]'}

尽管已获取目标并且替换组正确,但方括号仍然存在,并且在它们之间,每个单个字符都已被替换组替换。我的正则表达式有什么问题以及如何获得所需的输出?

编辑:

感谢 Joshua Varghese 的回答,我必须明确指出,方括号之间可能不仅有关键引用。例如:

d = {'a_key': 'a_value', 'b_key': '[ a_key ]+1/[a_key]+[another_thing ]'}

这里我不想替换[another_thing]。通过尝试这个例子,我发现并不是所有的字符都被替换了。但是,在被替换的那些中,有空格和键中包含的任何字符。 [another_thing] 将变为 [a_valuenotha_valuera_valuethinga_value]

EDIT2:

感谢 WeavingBird1917 的评论,我将尝试使用类似下面的代码,而不是在 for _ in d: 循环中设置代码。但是,由于 dict 是无序的,所以我不知道如何完成递归函数。任何帮助表示赞赏。

from re import search, sub
d = {'a_key': '[c_key]', 'b_key': '1', 'c_key': '[b_key] + [e_key]*[another_thing ]', 'd_key': '[b_key]', 'e_key': '[b_key]'}

def rec(z):
    parameter = search(r"\[\s*(\w+)\s*\]", d[z])
    if parameter is not None and parameter.group(1) in d.keys():
        rec(parameter.group(1))
    else:
        print("+1")
        for k in d:
            d[k] = sub(r"\[\s*(\w+)\s*\]", lambda match: "(" + d[match.group(1)] + ")" if match.group(1) in d else match.group(), d[k])
        # need to go to the next key or to break if no other, but dict are unordered

rec(list(d.keys())[0])
print(d)

【问题讨论】:

  • 循环/链式引用是否可行? c_value可以引用b_value,b_value可以引用a_value等吗?
  • @WeavingBird1917 允许链接引用。循环引用可能不会,因为它们可能会循环一段时间。
  • @WeavingBird1917 你是对的。谢谢。我假设n 是键的数量。我是否必须在这种循环中设置整个代码:for _ in d:?它似乎有效,但正如你所说,它没有在迭代次数方面进行优化。
  • @WeavingBird1917 我用一个递归函数编辑了这个问题。但是,如何处理 dict 中缺少顺序的问题?
  • 我添加了一个使用递归的解决方案。

标签: python regex dictionary


【解决方案1】:

我相信创建的分组会产生问题,因为文本由于分组而被拆分。
所以用没有组的模式替换它:

from re import search, sub
d = {'a_key': 'a_value', 'b_key': '[ a_key ]+1/[a_key]'}
for k in d.keys():
    parameter = search("\[\s*(\w+)\s*\]", d[k])
    if parameter is not None and parameter.group(1) in d.keys():
        print("target: "+parameter.group())
        print("to be replaced by: "+d[parameter.group(1)])
        d[k] = sub("\[\s*\w+\s*\]", d[parameter.group(1)], d[k])
print(d)

编辑
以下是解决方案:

d = {'a_key': 'a_value', 'b_key': '[ a_key ]+1/[a_key]+[another_thing ]'}

这里,我们将使用here中采用的方法:

for k in d.keys():
    d[k] = sub("\[\s*(\w+)\s*\]", lambda match: d[match.group(1)] if match.group(1) in d else match.group(), d[k])

或者简单地说:

for k in d.keys():
    d[k] = sub("\[\s*(\w+)\s*\]", lambda match: d.get(match.group(1), match.group()), d[k])

给予:

{'a_key': 'a_value', 'b_key': 'a_value+1/a_value+[another_thing ]'}

【讨论】:

  • 感谢您的回答。如果方括号之间只有关键引用,您的方法将起作用。如果还有其他引用,它们也将被替换。这就是我试图用“if”条件避免的。无论如何,我应该更准确,我会相应地编辑我的问题。
  • 我之前看到了一条评论,但它已经消失了。好吧,使用 sub() 不是强制性的。欢迎在一般情况下提供所需输出的任何方法。
  • 它有效。谢谢。我建议您在我接受之前编辑您的答案:最好将d[match.group(1)] 替换为"("+d[match.group(1)]+")" 以处理诸如'c_key': '2*[ b_key ]' 之类的情况。
  • @someone that edit doesn't answer the question :) 这是一个编辑你用来修改我的答案!现在我已经回答了这个问题:D
  • 我不认为禁止给出比要求的答案更笼统的答案。我什至认为它对像我一样一开始没有想到可能发生这种问题的其他人很有用。无论如何,我可以按原样接受答案。再次感谢。
【解决方案2】:

由于允许多个链式引用(请参阅 cmets),因此这是一个递归工作的解决方案。可以通过跟踪一组访问过的键来改进它,以避免在 get_reference 内再次调用 process_value

import re    

def get_reference(_dict, match):
    reference = match.group(1)
    if reference in _dict:
        return process_value(_dict, reference)
    else:
        return match.group(0)

def process_value(_dict, key):
    new_value = re.sub("\[\s*(\w+)\s*\]", 
                       lambda match: get_reference(_dict, match), _dict[key])
    _dict[key] = new_value

    return new_value

def process_dict(_dict):
    for key in _dict:
        process_value(_dict, key)

输入/输出示例:

example_dict = dict(a_key="[c_key ]", b_key="1", c_key="[b_key] + [ e_key ]",
      d_key="[b_key]", e_key="[b_key]", f_key="3", g_key="2 + [h_key]", 
      h_key="[b_key] / [ k_key ]")

process_dict(example_dict)

print(example_dict)
# Output: 
# {'a_key': '1 + 1',
#  'b_key': '1',
#  'c_key': '1 + 1',
#  'd_key': '1',
#  'e_key': '1',
#  'f_key': '3',
#  'g_key': '2 + 1 / [ k_key ]',
#  'h_key': '1 / [ k_key ]'}

【讨论】:

  • 谢谢。有用。我将使用r"\[\s*(\w+)\s*\]" 而不是"\[\s*(\w+)\s*\]" 来防止来自我的IDE 的警告,并使用return "("+new_value+")" 而不是return new_value 来处理我在Joshua Varghese 的回答中提到的潜在问题。
猜你喜欢
  • 2013-01-10
  • 2018-07-13
  • 1970-01-01
  • 1970-01-01
  • 2015-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多