如何映射两个字符串之间的差异?

Bil*_*lly 5 python regex string python-3.x

我遇到了以下问题,并想知道解决它的优雅方法是什么.假设我们有两个字符串:

string1 = "I love to eat $(fruit)"
string2 = "I love to eat apples"
Run Code Online (Sandbox Code Playgroud)

这些字符串之间的唯一区别是$(fruit)apples.所以,我发现水果是苹果,dict{fruit:apples}可以归还.

另一个例子是:

string1 = "I have $(food1), $(food2), $(food3) for lunch"
string2 = "I have rice, soup, vegetables for lunch"
Run Code Online (Sandbox Code Playgroud)

我希望得到一个dict{food1:rice, food2:soup, food3:vegetables}结果.

任何人都对如何实现它有一个好主意?

编辑:

我想我需要功能更强大.

ex.
string1 = "I want to go to $(place)"
string2 = "I want to go to North America"

result: {place : North America}

ex.
string1 = "I won $(index)place in the competition"
string2 = "I won firstplace in the competition"

result: {index : first}
Run Code Online (Sandbox Code Playgroud)

规则将是:映射字符串的不同部分并使它们成为一个字典

所以我猜所有使用str.split()或尝试拆分字符串的答案都行不通.没有规则说明哪些字符将用作字符串中的分隔符.

cs9*_*s95 5

我认为这可以通过基于正则表达式的拆分来干净地完成.这还应该处理标点符号和其他特殊字符(其中空格分割是不够的).

import re

p = re.compile(r'[^\w$()]+')
mapping = {
    x[2:-1]: y for x, y in zip(p.split(string1), p.split(string2)) if x != y}
Run Code Online (Sandbox Code Playgroud)

对于您的示例,这将返回

{'fruit': 'apple'}
Run Code Online (Sandbox Code Playgroud)

{'food1': 'rice', 'food2': 'soup', 'food3': 'vegetable'}
Run Code Online (Sandbox Code Playgroud)


Gia*_*tta 1

一种解决方案是将其替换$(name)为正(?P<name>.*)则表达式并将其用作正则表达式:

def make_regex(text):
    replaced = re.sub(r'\$\((\w+)\)', r'(?P<\1>.*)', text)
    return re.compile(replaced)

def find_mappings(mapper, text):
    return make_regex(mapper).match(text).groupdict()
Run Code Online (Sandbox Code Playgroud)

使用示例:

>>> string1 = "I have $(food1), $(food2), $(food3) for lunch"
>>> string2 = "I have rice, soup, vegetable for lunch"
>>> string3 = "I have rice rice rice, soup, vegetable for lunch"
>>> make_regex(string1).pattern
'I have (?P<food1>.*), (?P<food2>.*), (?P<food3>.*) for lunch'
>>> find_mappings(string1, string2)
{'food1': 'rice', 'food3': 'vegetable', 'food2': 'soup'}
>>> find_mappings(string1, string3)
{'food1': 'rice rice rice', 'food3': 'vegetable', 'food2': 'soup'}
Run Code Online (Sandbox Code Playgroud)

请注意,这可以处理非字母数字标记(请参阅food1rice rice rice)。显然,这可能会进行大量的回溯并且可能会很慢。您可以.*根据您对“令牌”的期望调整正则表达式以尝试使其更快。


对于生产就绪的代码,您需要组re.escape之外的部分(?P<name>.*)。这样做有点麻烦,因为你必须“拆分”该字符串并调用re.escape每一部分,然后将它们放在一起并调用re.compile


由于我的答案被接受,我想包含一个更强大的正则表达式版本:

def make_regex(text):
    regex = ''.join(map(extract_and_escape, re.split(r'\$\(', text)))
    return re.compile(regex)

def extract_and_escape(partial_text):
    m = re.match(r'(\w+)\)', partial_text)
    if m:
        group_name = m.group(1)
        return ('(?P<%s>.*)' % group_name) + re.escape(partial_text[len(group_name)+1:])
    return re.escape(partial_text)
Run Code Online (Sandbox Code Playgroud)

这可以避免文本包含特殊正则表达式字符时出现的问题(例如I have $(food1) and it costs $$$。第一个解决方案最终会被视为锚点$$$的三倍$(这会失败),这个强大的解决方案可以避免这些问题。