2013-10-15 73 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

這種方法如何:將新行存儲爲'x',分步遍歷每行,並用'':''分隔。如果第二部分不是空的,則將這兩對作爲鍵和值添加到字典中,並從'x'中彈出該行;接下來,您將只剩下一個鍵列表(帶有':'),而其他所有內容都會列在該鍵的列表中。運行修剪過的'x'並將剩下的添加到字典中。 –

+0

爲什麼「住在電視機裏」的規則最終不在列表中?或者,「農民法案」呢? – 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())) 

輸出結果爲:

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

我覺得這已經夠複雜了,它看上去就像一個明確的狀態機清洗劑,但我想在任何新手能理解的術語來寫這個。

+0

謝謝,這是一個非常乾淨的解決方案。我最初試圖用正則表達式來解決這個問題,但是也許這個正則表達式不值得付出努力,而且會帶來更多的複雜性。 – user2152283

+0

@ user2152283:每當我無法弄清楚如何用正則表達式做某些事情(即使我確定它是一種我正在嘗試解析的常規語言),我會退後一步,嘗試以另一種方式寫它。有時候,我可以在潛意識中找出正則表達式;有時這意味着我最終得到了一個基於非正則表達式但可讀的解析器;有時候我最終會向自己證明語言不規律,甚至是語境敏感,我將需要更復雜的東西......但無論如何,這是一場勝利。 – 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