У меня есть наивный "парсер", который просто делает что-то вроде:
[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 позже предлагается в несколько более подробной форме;

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:

Просто чтобы удовлетворить мое любопытство, возможно ли это на самом деле с помощью pure регулярных выражений? Т.е. чтобы re.findall() возвращал список из двух кортежей?

8
Kimvais 1 Фев 2013 в 11:41

4 ответа

Могу ли я предложить вам использовать операции разбиения, как и раньше. Но сначала разделите на «равные», затем разделите на крайнюю правую запятую, чтобы составить единый список левой и правой строк.

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]))
1
daramarak 1 Фев 2013 в 09:20

Можете ли вы попробовать это, это сработало для меня:

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']

Тогда вы можете просто перейти по списку и делать то, что вы хотите.

0
iblazevic 1 Фев 2013 в 13:00

Предполагая, что имя ключа никогда не содержит ,, вы можете разбить на ,, когда следующая последовательность без , и = заменяется =.

re.split(r',(?=[^,=]+=)', inputString)

(Это то же самое, что и мое первоначальное решение. Я ожидаю, что будет использоваться re.split, а не re.findall или str.split).

Полное решение может быть сделано в одну строку:

[re.findall('(.*?)=(.*)', token)[0] for token in re.split(r',(?=[^,=]+=)', inputString)]
0
nhahtdh 1 Фев 2013 в 13:23

Ответ дарамарака либо почти работает, либо работает как есть; Трудно понять, как форматируется пример выходных данных и расплывчатые описания шагов. Но если это очень близкая к работе версия, ее легко исправить.

Поместив это в код:

>>> bits=[x.rsplit(',', 1) for x in s.split('=')]
>>> kv = [(bits[i][-1], bits[i+1][0]) for i in range(len(bits)-1)]

Первая строка - это (я верю) ответ Дарамарака. Сама по себе первая строка дает вам пары (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')]
4
abarnert 1 Фев 2013 в 08:27