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()或尝试拆分字符串的答案都行不通.没有规则说明哪些字符将用作字符串中的分隔符.
我认为这可以通过基于正则表达式的拆分来干净地完成.这还应该处理标点符号和其他特殊字符(其中空格分割是不够的).
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)
一种解决方案是将其替换$(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)
请注意,这可以处理非字母数字标记(请参阅food1和rice 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 $$$。第一个解决方案最终会被视为锚点$$$的三倍$(这会失败),这个强大的解决方案可以避免这些问题。