2010-03-01 92 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。