2010-03-01 43 views
10

我期待加快我在這裏的發現過程,因爲這是我進入詞彙分析世界的第一次冒險。也許這是錯誤的道路。首先,我將描述我的問題:Python - 詞法分析和標記化

我有很大的屬性文件(以1,000個屬性的順序排列),當它們被提取時,它們實際上只是大約15個重要屬性,其餘的可以生成或很少更改。

因此,舉例來說:

general { 
    name = myname 
    ip = 127.0.0.1 
} 

component1 { 
    key = value 
    foo = bar 
} 

這是我想創造來標記類似的格式類型:

property.${general.name}blah.home.directory = /blah 
property.${general.name}.ip = ${general.ip} 
property.${component1}.ip = ${general.ip} 
property.${component1}.foo = ${component1.foo} 

property.mynameblah.home.directory = /blah 
property.myname.ip = 127.0.0.1 
property.component1.ip = 127.0.0.1 
property.component1.foo = bar 

詞法分析和符號化聽起來像我最好的路線,但這是一個非常簡單的形式。這是一個簡單的語法,一個簡單的替換,我想確保我沒有帶大錘敲釘子。

我可以創建自己的詞法分析器和標記器,或ANTlr是一種可能性,但我不喜歡重新發明輪子,ANTlr聽起來像是過度殺傷。

我對編譯器技術並不熟悉,所以在正確的方向指針&代碼將不勝感激。

注意:我可以改變輸入格式。

+2

爲什麼不使用的,而不是創建自己的解析器JSON ?? – AndiDog 2010-03-01 20:39:12

+0

您的示例翻譯似乎有一些錯誤。如果沒有,我不明白爲什麼在示例的第3行中將「$ {component1} .ip」轉換爲「component1」。 如果語法是正則表達式,我可能會用正則表達式翻譯$ {identifiers},並用沒有字典條目的字典查找來替換它們。 – msw 2010-03-01 20:42:15

+0

那裏有一些錯誤,我想我已經糾正了它們。 – 2010-03-01 20:45:26

回答

10

關於Using Regular Expressions for Lexical Analysiseffbot.org有一篇很好的文章。

適應記號賦予你的問題:

import re 

token_pattern = r""" 
(?P<identifier>[a-zA-Z_][a-zA-Z0-9_]*) 
|(?P<integer>[0-9]+) 
|(?P<dot>\.) 
|(?P<open_variable>[$][{]) 
|(?P<open_curly>[{]) 
|(?P<close_curly>[}]) 
|(?P<newline>\n) 
|(?P<whitespace>\s+) 
|(?P<equals>[=]) 
|(?P<slash>[/]) 
""" 

token_re = re.compile(token_pattern, re.VERBOSE) 

class TokenizerException(Exception): pass 

def tokenize(text): 
    pos = 0 
    while True: 
     m = token_re.match(text, pos) 
     if not m: break 
     pos = m.end() 
     tokname = m.lastgroup 
     tokvalue = m.group(tokname) 
     yield tokname, tokvalue 
    if pos != len(text): 
     raise TokenizerException('tokenizer stopped at pos %r of %r' % (
      pos, len(text))) 

爲了測試它,我們這樣做:

stuff = r'property.${general.name}.ip = ${general.ip}' 
stuff2 = r''' 
general { 
    name = myname 
    ip = 127.0.0.1 
} 
''' 

print ' stuff '.center(60, '=') 
for tok in tokenize(stuff): 
    print tok 

print ' stuff2 '.center(60, '=') 
for tok in tokenize(stuff2): 
    print tok 

爲:

========================== stuff =========================== 
('identifier', 'property') 
('dot', '.') 
('open_variable', '${') 
('identifier', 'general') 
('dot', '.') 
('identifier', 'name') 
('close_curly', '}') 
('dot', '.') 
('identifier', 'ip') 
('whitespace', ' ') 
('equals', '=') 
('whitespace', ' ') 
('open_variable', '${') 
('identifier', 'general') 
('dot', '.') 
('identifier', 'ip') 
('close_curly', '}') 
========================== stuff2 ========================== 
('newline', '\n') 
('identifier', 'general') 
('whitespace', ' ') 
('open_curly', '{') 
('newline', '\n') 
('whitespace', ' ') 
('identifier', 'name') 
('whitespace', ' ') 
('equals', '=') 
('whitespace', ' ') 
('identifier', 'myname') 
('newline', '\n') 
('whitespace', ' ') 
('identifier', 'ip') 
('whitespace', ' ') 
('equals', '=') 
('whitespace', ' ') 
('integer', '127') 
('dot', '.') 
('integer', '0') 
('dot', '.') 
('integer', '0') 
('dot', '.') 
('integer', '1') 
('newline', '\n') 
('close_curly', '}') 
('newline', '\n') 
+0

Fyi,[這種標記器](http://docs.python.org/3.2/library/re.html#writing-a-tokenizer)將其加入到're'模塊的stdlib文檔中 – cfi 2013-04-22 09:13:55

1

如果您可以更改輸入文件的格式,那麼您可以使用解析器來處理現有的格式,例如JSON。

但是,從您的問題聲明,它聽起來像是不是這樣。所以如果你想創建一個自定義詞法分析器和解析器,使用PLY(Python Lex/Yacc)。它很容易使用,和lex/yacc一樣工作。

以下是使用PLY構建的計算器的example的鏈接。請注意,以t_開頭的所有內容都是一個詞法分析規則 - 定義一個有效的令牌 - 並且以p_開頭的所有內容都是定義語法產生的解析器規則。

2

就像你的格式看起來那麼簡單,我認爲一個完整的解析器/詞法分析器會過度殺傷。看起來像正則表達式和字符串操作的組合可以做到這一點。

另一個想法是將文件更改爲類似json或xml的文件,並使用現有的軟件包。

2

一個簡單的DFA適用於此。你只需要少數幾個州:

  1. 尋找${
  2. ${尋找至少一個有效的字符構成的名稱
  3. 至少看到一個有效的名稱字符,尋找更多的名稱的字符或}

如果屬性文件與訂單不可知,您可能需要雙通道處理器來驗證每個名稱是否正確解析。

當然,您需要編寫替換代碼,但是一旦您有所有使用名稱的列表,最簡單的可能實現就是在${name}上找到/替換其相應的值。

1

您提供的語法與Mako templates engine類似。我認爲你可以試試看,這是相當簡單的API。