2013-10-15 3 views
0

У меня есть следующий текст: кусокРазбор двоеточиями данных

string = """ 
    apples: 20 
    oranges: 30 
    ripe: yes 
    farmers: 
      elmer fudd 
        lives in tv 
      farmer ted 
        lives close 
      farmer bill 
        lives far 
    selling: yes 
    veggies: 
      carrots 
      potatoes 
    """ 

Я пытаюсь найти хороший регулярное выражение, которое позволит мне разобрать ключевые ценности. Я могу захватить значения один ключ линии что-то вроде:

'(.+?):\s(.+?)\n' 

Однако проблема возникает, когда я ударил фермеров, или овощи.

Используя повторно флаги, мне нужно сделать что-то вроде:

re.findall('(.+?):\s(.+?)\n', string, re.S), 

Однако я с чертовски времени, захватывая все ценности, связанные с фермерами.

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

и цель состоит в том, чтобы иметь что-то вроде:

{ 'apples': 20, 'farmers': ['elmer fudd', 'farmer ted'] } 

т.д.

Заранее спасибо за вашу помощь.

+0

Является ли значительная часть «жизни в ТВ»? Вы не упоминали об этом в своем желаемом выходе. –

+0

Как насчет этого подхода: split by newlines хранится как 'x', пройдите через каждую строку и разделите его на' ':' '. Если вторая часть не пуста, добавьте две пары в качестве ключа и значения в словарь и введите строку из 'x'; далее вам останется список только ключей (с ':'), и все остальное будет в списке для этого ключа. Пройдите через обрезанный 'x' и добавьте оставшееся в словарь. –

+0

Какое правило почему '' живет в tv'' не входит в список? Или '' счет фермера '', если на то пошло? – abarnert

ответ

1

Вот действительно тупой парсер, который учитывает ваши (кажущиеся) правила отступа:

def parse(s): 
    d = {} 
    lastkey = None 
    for fullline in s: 
     line = fullline.strip() 
     if not line: 
      pass 
     elif ':' not in line: 
      indent = len(fullline) - len(fullline.lstrip()) 
      if lastindent is None: 
       lastindent = indent 
      if lastindent == indent: 
       lastval.append(line) 
     else: 
      if lastkey: 
       d[lastkey] = lastval 
       lastkey = None 
      if line.endswith(':'): 
       lastkey, lastval, lastindent = key, [], None 
      else: 
       key, _, value = line.partition(':') 
       d[key] = value.strip() 
    if lastkey: 
     d[lastkey] = lastval 
     lastkey = None 
    return d 

import pprint 
pprint(parse(string.splitlines())) 

Выход:

Я думаю, что это уже достаточно сложно, что он будет выглядеть более чистым, чем явный государственный автомат, но я хотел написать это в терминах, которые мог понять любой новичок.

+0

Спасибо, это очень чистое решение. Сначала я пытался решить это с помощью регулярного выражения, но, возможно, регулярное выражение не стоит усилий и просто требует большей сложности. – user2152283

+0

@ user2152283: Всякий раз, когда я не могу понять, как что-то делать с регулярным выражением (даже если я уверен, что это обычный язык, который я пытаюсь разобрать), я отступаю и пытаюсь записать его по-другому. Иногда это позволяет мне понять регулярное выражение подсознательно; иногда это означает, что я получаю не-regexp-based, но читаемый синтаксический анализатор; иногда я в конечном итоге доказываю, что язык нерегулярный или даже контекстно-зависимый, и мне понадобится что-то более сложное ... но, несмотря ни на что, это победа. – abarnert

2

Вы можете посмотреть на PyYAML, этот текст очень близок, если не действителен YAML.

+0

Это близко, но я считаю, что «фермеры» получат одну длинную строку - это не совсем список ... –

+0

Или потенциально выбросить ошибку синтаксического анализа. –

+0

Если можно захватить значения, я мог бы разделить на новую строку и построить список. Тем не менее, пытаясь выяснить, как наилучшим образом захватить ценности. – user2152283

1

Вот совершенно глупый способ сделать это:

import collections 


string = """ 
    apples: 20 
    oranges: 30 
    ripe: yes 
    farmers: 
      elmer fudd 
        lives in tv 
      farmer ted 
        lives close 
      farmer bill 
        lives far 
    selling: yes 
    veggies: 
      carrots 
      potatoes 
    """ 


def funky_parse(inval): 
    lines = inval.split("\n") 
    items = collections.defaultdict(list) 
    at_val = False 
    key = '' 
    val = '' 
    last_indent = 0 
    for j, line in enumerate(lines): 
     indent = len(line) - len(line.lstrip()) 
     if j != 0 and at_val and indent > last_indent > 4: 
      continue 
     if j != 0 and ":" in line: 
      if val: 
       items[key].append(val.strip()) 
      at_val = False 
      key = '' 
     line = line.lstrip() 
     for i, c in enumerate(line, 1): 
      if at_val: 
       val += c 
      else: 
       key += c 
      if c == ':': 
       at_val = True 
      if i == len(line) and at_val and val: 
       items[key].append(val.strip()) 
       val = '' 
     last_indent = indent 

    return items 

print dict(funky_parse(string)) 

ВЫВОД

{'farmers:': ['elmer fudd', 'farmer ted', 'farmer bill'], 'apples:': ['20'], 'veggies:': ['carrots', 'potatoes'], 'ripe:': ['yes'], 'oranges:': ['30'], 'selling:': ['yes']} 
+0

Спасибо, это решение работает. – user2152283

Смежные вопросы