我有一个天真“分析器”,简单地做一些事情,如:
[x.split('=') for x in mystring.split(',')]
然而MyString的可以是这样的
'foo=bar,breakfast=spam,eggs'
明显,
天真的分流将只是没有做到这一点。 我限制到Python 2.6的标准库为此,
因此,例如pyparsing不能使用。
预计产量
[('foo', 'bar'), ('breakfast', 'spam,eggs')]
我试图用正则表达式来做到这一点,但现在面临以下问题:
我第一次尝试
r'([a-z_]+)=(.+),?'
给我
[('foo', 'bar,breakfast=spam,eggs')]
明显,
制作.+
非贪婪不解决问题。
所以,
我猜我必须以某种方式使最后一个逗号(或$
)的强制性。
正是这样做并没有真正的工作,
r'([a-z_]+)=(.+?)(?:,|$)'
如与在含有一种的值的逗号后面的东西被省略,
例如[('foo', 'bar'), ('breakfast', 'spam')]
我想,我必须用某种向后看(?)的操作。
这个问题(S)
我该使用1. 哪一个 ? 要么
2.我如何做到这一点/这一点?
编辑 :
基于daramarak下面的回答,
我落得这样做几乎同样的事情abarnert后建议在一个稍微更详细的形式;
vals = [x.rsplit(',', 1) for x in (data.split('='))]
ret = list()
while vals:
value = vals.pop()[0]
key = vals[-1].pop()
ret.append((key, value))
if len(vals[-1]) == 0:
break
编辑2:
只是为了满足我的好奇心,这是实际上是可能的纯正则表达式? 即,使得re.findall()
将返回的2元组的列表?
Answer 1:
只是为了便于比较,这里似乎要解决的问题还有一个正则表达式:
([^=]+) # key
= # equals is how we tokenise the original string
([^=]+) # value
(?:,|$) # value terminator, either comma or end of string
这里的技巧来限制你在你的第二组捕捉什么。 .+
吞下=
标志,这是我们可以使用从值区分键的字符。 完整的正则表达式不依赖于任何回溯(所以它应该是的东西,如兼容RE2 ,如果这是可取的),并可以在abarnert的例子工作。
使用方法如下:
re.findall(r'([^=]+)=([^=]+)(?:,|$)', 'foo=bar,breakfast=spam,eggs,blt=bacon,lettuce,tomato,spam=spam')
返回:
[('foo', 'bar'), ('breakfast', 'spam,eggs'), ('blt', 'bacon,lettuce,tomato'), ('spam', 'spam')]
Answer 2:
daramarak的回答要么非常接近的作品,或作品原样; 很难从样本输出格式化的方式和步骤的模糊描述判断。 但是,如果它是极近,作品版本,它很容易修复。
付诸代码:
>>> bits=[x.rsplit(',', 1) for x in s.split('=')]
>>> kv = [(bits[i][-1], bits[i+1][0]) for i in range(len(bits)-1)]
第一行是(我相信)daramarak的答案。 就其本身而言,第一行让你对(value_i, key_i+1)
而不是(key_i, value_i)
第二行是为最明显的修复。 随着越来越多的中间步骤,有点输出,看看它是如何工作的:
>>> s = 'foo=bar,breakfast=spam,eggs,blt=bacon,lettuce,tomato,spam=spam'
>>> bits0 = s.split('=')
>>> bits0
['foo', 'bar,breakfast', 'spam,eggs,blt', 'bacon,lettuce,tomato,spam', 'spam']
>>> bits = [x.rsplit(',', 1) for x in bits0]
>>> bits
[('foo'), ('bar', 'breakfast'), ('spam,eggs', 'blt'), ('bacon,lettuce,tomato', 'spam'), ('spam')]
>>> kv = [(bits[i][-1], bits[i+1][0]) for i in range(len(bits)-1)]
>>> kv
[('foo', 'bar'), ('breakfast', 'spam,eggs'), ('blt', 'bacon,lettuce,tomato'), ('spam', 'spam')]
Answer 3:
我可以建议您使用分裂操作如前。 但在分割等号,再在最右边的逗号分割,使左,右弦的一个列表。
input =
"bob=whatever,king=kong,banana=herb,good,yellow,thorn=hurts"
将在第一次分裂成为
first_split = input.split("=")
#first_split = ['bob' 'whatever,king' 'kong,banana' 'herb,good,yellow,thorn' 'hurts']
然后在最右边的逗号分割为您提供:
second_split = [single_word for sublist in first_split for item in sublist.rsplit(",",1)]
#second_split = ['bob' 'whatever' 'king' 'kong' 'banana' 'herb,good,yellow' 'thorn' 'hurts']
那么你只要收集对这样的:
pairs = dict(zip(second_split[::2],second_split[1::2]))
Answer 4:
你可以试试这个,它为我工作:
mystring = "foo=bar,breakfast=spam,eggs,e=a"
n = []
i = 0
for x in mystring.split(','):
if '=' not in x:
n[i-1] = "{0},{1}".format(n[i-1], x)
else:
n.append(x)
i += 1
print n
你得到类似结果:
['foo=bar', 'breakfast=spam,eggs', 'e=a']
然后,你可以简单地去了名单,你想要做什么。
Answer 5:
假设键的名称决不会包含,
你可以在拆分,
当不下一序列,
并=
被成功=
。
re.split(r',(?=[^,=]+=)', inputString)
(这是一样的我原来的解决方案。我希望re.split
使用,而不是re.findall
或str.split
)。
将完整的解决方案可以在一个班轮来完成:
[re.findall('(.*?)=(.*)', token)[0] for token in re.split(r',(?=[^,=]+=)', inputString)]
文章来源: Regular expression to match comma separated list of key=value where value can contain commas