我有一个天真的“解析器”,只是做了类似的事情:
[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')]
我想我必须使用某种后视(?)操作
问题
1. 我使用哪一个?或
2. 如何我这样做/这个?
修改:
根据daramarak下面的回答,
我最后做了与abarnert稍后suggested完全相同的事情,稍微冗长一点;
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元组列表?
答案 0 :(得分:10)
仅用于比较目的,这里的正则表达式似乎也解决了这个问题:
([^=]+) # 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')]
答案 1 :(得分:4)
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')]
答案 2 :(得分:1)
我可以建议您像以前一样使用拆分操作。但首先分成等号,然后在最右边的逗号分割,以制作一个左右字符串的单个列表。
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]))
答案 3 :(得分:0)
你能试试吗,它对我有用:
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']
然后你可以简单地浏览列表并做你想做的事。
答案 4 :(得分:0)
假设密钥的名称从不包含,
,当,
的下一个序列由,
和=
继承时,您可以在=
分割}。
re.split(r',(?=[^,=]+=)', inputString)
(这与我原来的解决方案相同。我希望使用re.split
,而不是re.findall
或str.split
。
完整的解决方案可以在单行中完成:
[re.findall('(.*?)=(.*)', token)[0] for token in re.split(r',(?=[^,=]+=)', inputString)]