の解析速度を向上させる私はは、区切られた構成ブロック
#start <some-name> ... #end <some-name>
で区切られたブロックで構成され、非常に大きな設定ファイルを持っているsome-name
たブロックで同じである必要があります。ブロックは複数回出現することができますが、決してそれ自身の中には含まれません。特定のブロックには他のブロックだけが表示されることがあります。私はこれらのブロックに興味がありませんが、第2レベルのブロックには興味があります。
実際のファイルでは、名前はblockX
で始まらず、お互いに非常に異なっています。
例:
#start block1
#start block2
/* string but no more name2 or name1 in here */
#end block2
#start block3
/* configuration data */
#end block3
#end block1
これは正規表現で解析され、非常に高速、付属のデバッガなしで実行すると、あるされています。以下のような単純なルールに
2kの
2.7メガバイトのファイルのための0.23s:
blocks2 = re.findAll('#start block2\s+(.*?)#end block2', contents)
私はpyparsingでこれを解析しようとしたが、速度も添付デバッガなしで非常に遅いですが、それは同じファイルに対して16秒を要しました。
私のアプローチは、正規表現からの単純な解析を模倣するような標準的なコードを生成することでしたので、今は他のコードを使用して、今すぐすべてのブロックを解析する必要はありません。文法はかなり拡張されています。ここで
は、私は私が間違っているのは何
block = [Group(Keyword(x) + SkipTo(Keyword('#end') + Keyword(x)) + Keyword('#end') - x)(x + '*') for x in ['block3', 'block4', 'block5', 'block6', 'block7', 'block8']]
blocks = Keyword('#start') + block
x = OneOrMore(blocks).searchString(contents) # I also tried parseString() but the results were similar.
を試してみました何ですか?どのように正規表現の実装で達成される速度に近いところに来るようにこれを最適化できますか?
編集:前の例では、実際のデータに比べて簡単に行く途中だったので、私は今、適切なものを作成した。このため
/* all comments are C comments */
VERSION 1 0
#start PROJECT project_name "what is it about"
/* why not another comment here too! */
#start SECTION where_the_wild_things_are "explain this section"
/* I need all sections at this level */
/* In the real data there are about 10k of such blocks.
There are around 10 different names (types) of blocks */
#start INTERFACE_SPEC
There can be anything in the section. Not Really but i want to skip anything until the matching (hash)end.
/* can also have comments */
#end INTERFACE_SPEC
#start some_other_section
name 'section name'
#start with_inner_section
number_of_points 3 /* can have comments anywhere */
#end with_inner_section
#end some_other_section /* basically comments can be anywhere */
#start some_other_section
name 'section name'
other_section_attribute X
ref_to_section another_section
#end some_other_section
#start another_section
degrees
#start section_i_do_not_care_about_at_the_moment
ref_to some_other_section
/* of course can have comments */
#end section_i_do_not_care_about_at_the_moment
#end another_section
#end SECTION
#end PROJECT
を私は自分のオリジナルの提案を展開していました。私は外側の2つのブロック(PROJECTとSECTION)をハードコーディングしています。
def test_parse(f):
import pyparsing as pp
import io
comment = pp.cStyleComment
start = pp.Literal("#start")
end = pp.Literal("#end")
ident = pp.Word(pp.alphas + "_", pp.printables)
inner_ident = ident.copy()
inner_start = start + inner_ident
inner_end = end + pp.matchPreviousLiteral(inner_ident)
inner_block = pp.Group(inner_start + pp.SkipTo(inner_end) + inner_end)
version = pp.Literal('VERSION') - pp.Word(pp.nums)('major_version') - pp.Word(pp.nums)('minor_version')
project = pp.Keyword('#start') - pp.Keyword('PROJECT') - pp.Word(pp.alphas + "_", pp.printables)(
'project_name') - pp.dblQuotedString + pp.ZeroOrMore(comment) - \
pp.Keyword('#start') - pp.Keyword('SECTION') - pp.Word(pp.alphas, pp.printables)(
'section_name') - pp.dblQuotedString + pp.ZeroOrMore(comment) - \
pp.OneOrMore(inner_block) + \
pp.Keyword('#end') - pp.Keyword('SECTION') + \
pp.ZeroOrMore(comment) - pp.Keyword('#end') - pp.Keyword('PROJECT')
grammar = pp.ZeroOrMore(comment) - version.ignore(comment) - project.ignore(comment)
with io.open(f) as ff:
return grammar.parseString(ff.read())
EDIT::時間は〜16秒のままであるこのバージョンで
タイプミスは、それが2Kと言ったが、それは代わりに、2.7メガバイトのファイルです。すべての
'matchPreviousLiteral'のヒントをありがとう。他の機能と同様に、http://pyparsing.wikispaces.com/HowToUsePyparsingには記載されていません。それ以上の最新のドキュメントはありますか? – RedX
https://pythonhosted.org/pyparsing/で、各リリースのオンラインドキュメントを最新の状態に保ちます。 http://infohost.nmt.edu/tcc/help/pubs/pyparsing/web/index.htmlのJohn Shipmanのページも参考になりますが、少し意見があります。文法のクリーンアップに関する推奨事項については、上記の私の編集を参照してください。 – PaulMcG
私が最初に述べたファイルサイズは1000だけずれていました.2kBではなく、最初から2.7MBでした。申し訳ありません。私は、このプロジェクトでは、より良い結果が得られるはずですが、時間差はあまりにも大きいので、私はこのプロジェクトでは、pyparsingを使用することができなくなると思います。 – RedX