正则表达式匹配逗号分隔的键=值列表,其中值可以包含逗号(Regular expression to

2019-07-19 02:53发布

我有一个天真“分析器”,简单地做一些事情,如:
[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.findallstr.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