12

これは字句解析の世界への私の最初のベンチャーであるため、ここでの発見プロセスをかなりスピードアップすることを目指しています。多分これは間違った道ですらあります。まず、私の問題について説明します。

私は非常に大きなプロパティファイル(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はやり過ぎのように聞こえます。

私はコンパイラのテクニックに精通していないので、正しい方向とコードへのポインタが最もありがたいです。

:入力形式は変更できます。

4

5 に答える 5

14

effbot.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')
于 2010-03-01T22:36:45.073 に答える
4

これには、単純なDFAが適しています。必要なのはいくつかの状態だけです。

  1. 探している${
  2. ${名前を形成する少なくとも1つの有効な文字を探しているのを見た
  3. 少なくとも1つの有効な名前文字が表示され、さらに名前文字またはを探しています}

プロパティファイルが順序に依存しない場合は、2パスプロセッサで各名前が正しく解決されることを確認する必要があります。

もちろん、次に置換コードを作成する必要がありますが、使用するすべての名前のリストを取得したら、可能な最も簡単な実装は、${name}対応する値での検索/置換です。

于 2010-03-01T21:22:52.603 に答える
2

あなたのフォーマットが単純であるように思われるので、私は完全なパーサー/レクサーはかなりやり過ぎだと思います。正規表現と文字列操作の組み合わせでうまくいくようです。

もう1つのアイデアは、ファイルをjsonやxmlなどに変更し、既存のパッケージを使用することです。

于 2010-03-01T20:50:03.233 に答える
1

提供する構文は、 Makoテンプレートエンジンに似ているようです。試してみるといいと思います。かなりシンプルなAPIです。

于 2010-03-01T22:24:40.890 に答える
1

入力ファイルの形式を変更できる場合は、JSONなどの既存の形式のパーサーを使用できます。

ただし、問題の説明からはそうではないように思われます。したがって、カスタムレクサーとパーサーを作成する場合は、PLY(Python Lex / Yacc)を使用します。使いやすく、lex/yaccと同じように機能します。

これは、 PLYを使用して構築された計算機のへのリンクです。で始まるものt_はすべてレクサールール(有効なトークンを定義する)であり、で始まるものp_はすべて文法の生成を定義するパーサールールであることに注意してください。

于 2010-03-01T20:39:28.570 に答える