2011-03-07 3 views
5

Bazı özel veri yapılarını ayrıştırmam gerekiyor. Onlar böyle kabaca görünüyor bazı biraz benzeri-C formatında şunlardır:Kod nasıl ayrıştırılır (Python'da)?

Group("GroupName") { 
    /* C-Style comment */ 
    Group("AnotherGroupName") { 
     Entry("some","variables",0,3.141); 
     Entry("other","variables",1,2.718); 
    } 
    Entry("linebreaks", 
      "allowed", 
      3, 
      1.414 
     ); 
} 

Bu konuda gitmek için birkaç yollar geliyor. Düzenli ifadeler kullanarak kodu 'belirtebilirim'. Kodu bir defada okuyabiliyordum ve veri yapısını oluşturmak için bir durum makinesi kullanabiliyordum. Virgül-linebreaks'lardan kurtulabilir ve çizgiyi hatlarını okuyabilirim. Bu kodu yürütülebilir Python koduna dönüştüren bazı dönüşüm komut dosyaları yazabilirim.

Böyle dosyaları ayrıştırmak için güzel bir pythonic yolu var mı?
Ayrıştırma hakkında nasıl giderdiniz?

Bu, dosya biçimlerinin ayrıştırılması ve bu dosya biçimi hakkında çok fazla bilgi verilmemesi hakkında genel bir sorudur.

+3

[Bu makale] (http://nedbatchelder.com/text/python-parsers.html) ilginizi çekebilir. –

cevap

6

(Mark Tolonen, ben tıklayarak sonrası aracılığıyla geldiğinde "Yayınla Gönder" üzereydim), bu oldukça basittir - Aşağıdaki kod gömülü yorumları görmek:

data = """Group("GroupName") { 
    /* C-Style comment */ 
    Group("AnotherGroupName") { 
     Entry("some","variables",0,3.141); 
     Entry("other","variables",1,2.718); 
    } 
    Entry("linebreaks", 
      "allowed", 
      3, 
      1.414 
     ); 
} """ 

from pyparsing import * 

# define basic punctuation and data types 
LBRACE,RBRACE,LPAREN,RPAREN,SEMI = map(Suppress,"{}();") 
GROUP = Keyword("Group") 
ENTRY = Keyword("Entry") 

# use parse actions to do parse-time conversion of values 
real = Regex(r"[+-]?\d+\.\d*").setParseAction(lambda t:float(t[0])) 
integer = Regex(r"[+-]?\d+").setParseAction(lambda t:int(t[0])) 

# parses a string enclosed in quotes, but strips off the quotes at parse time 
string = QuotedString('"') 

# define structure expressions 
value = string | real | integer 
entry = Group(ENTRY + LPAREN + Group(Optional(delimitedList(value)))) + RPAREN + SEMI 

# since Groups can contain Groups, need to use a Forward to define recursive expression 
group = Forward() 
group << Group(GROUP + LPAREN + string("name") + RPAREN + 
      LBRACE + Group(ZeroOrMore(group | entry))("body") + RBRACE) 

# ignore C style comments wherever they occur 
group.ignore(cStyleComment) 

# parse the sample text 
result = group.parseString(data) 

# print out the tokens as a nice indented list using pprint 
from pprint import pprint 
pprint(result.asList()) 

Baskı

pyparsing çözümlenir jeton yapı kazandırmak için, bir "Grup" sınıfını tanımlar beri
[['Group', 
    'GroupName', 
    [['Group', 
    'AnotherGroupName', 
    [['Entry', ['some', 'variables', 0, 3.141]], 
    ['Entry', ['other', 'variables', 1, 2.718]]]], 
    ['Entry', ['linebreaks', 'allowed', 3, 1.4139999999999999]]]]] 

(ne yazık ki, bazı karışıklıklar olabilir - listesi ifadesi bir pyparsing Grubu içinde kapalı durumda olduğundan bir Entry değer listeleri gruplandırılmış da nasıl not .)

+3

Sadece O'Reilly kitapçıda 10 $ kazandınız! – bastibe

1

Ne sıklıkta buna ihtiyacınız olduğuna ve sözdiziminin aynı kalmasına bağlı olarak değişir. Eğer cevaplar "oldukça sık" ve "az ya da çok" ise, o zaman sözdizimini ifade etmenin ve PyPEG ya da LEPL gibi bir araçla o dile özel bir ayrıştırıcı yazmanın bir yolunu arardım. Ayrıştırıcı kurallarını tanımlamak büyük bir iştir, dolayısıyla aynı tür dosyaları ayrıştırmanız gerekmedikçe, genellikle etkili olmayabilir.

Ancak PyPEG sayfasına bakarsanız, ayrıştırılmış verilerin XML'ye nasıl çıkarılacağını size söyler, böylece bu araç size yeterli güç sağlamazsa, XML'yi oluşturmak için kullanabilir ve ardından ör. Xml'yi ayrıştırmak için lxml.