Python-词法分析和标记化

2024-09-28 20:20:00 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望在这里加快我的发现过程,因为这是我第一次进入词汇分析领域。也许这条路是错的。首先,我将描述我的问题:

我有非常大的属性文件(按1000个属性的顺序排列),这些文件经过提炼后,实际上只有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

词法分析和标记化听起来是我最好的方法,但这是一种非常简单的方法。这是一个简单的语法,一个简单的替换,我想确保我不会带着大锤去敲钉子。

我可以创建自己的lexer和tokenizer,或者ANTlr是一种可能性,但我不喜欢重新发明轮子,ANTlr听起来像是过度杀戮。

我不熟悉编译器技术,所以最希望指针指向正确的方向和代码。

注意:我可以更改输入格式。


Tags: 文件name标记iphome属性foo格式
3条回答

尽管您的格式看起来很简单,但我认为一个完整的解析器/词法分析器可能是过分了。似乎regex和字符串操作的结合可以做到这一点。

另一个想法是将文件更改为json或xml,并使用现有的包。

effbot.org有一篇关于Using Regular Expressions for Lexical Analysis的优秀文章。

使标记器适应您的问题:

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')

一个简单的DFA可以很好地解决这个问题。你只需要几个州:

  1. 寻找${
  2. 已看到${正在查找构成名称的至少一个有效字符
  3. 至少看到一个有效的名称字符,正在查找更多的名称字符或}

如果属性文件与顺序无关,则可能需要一个双通道处理器来验证每个名称是否正确解析。

当然,您需要编写替换代码,但是一旦您有了使用的所有名称的列表,最简单的可能实现是在${name}上使用相应的值查找/替换。

相关问题 更多 >