2017-12-01 23 views
3

実際には、シェルコマンドの出力である標準テキストを解析しようとしています。Perl6解析ファイル

pool: thisPool 
state: ONLINE 
status: Some supported features are not enabled on the pool. The pool can 
    still be used, but some features are unavailable. 
action: Enable all features using 'zpool upgrade'. Once this is done, 
    the pool may no longer be accessible by software that does not support 
    the features. See zpool-features(5) for details. 
    scan: none requested 
config: 

    NAME            STATE  READ WRITE CKSUM 
    homePool           ONLINE  0  0  0 
     mirror-0           ONLINE  0  0  0 
     ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7NUE93C  ONLINE  0  0  0 
     ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7RE2A4F  ONLINE  0  0  0 
    cache 
     ata-KINGSTON_SV300S37A60G_50026B7261025D7E-part3 ONLINE  0  0  0 

errors: No known data errors 

私はPerl6文法を使いたいと思います。それぞれのフィールドを別々のトークンまたは正規表現で取り込みたいと思います。だから、私は次の文法を作った:私の考えは、正規表現は、キーワードを見つけることである

grammar zpool { 
     regex TOP { \s+ [ <keyword> <collection> ]+ } 
     token keyword { "pool: " | "state: " | "status: " | "action: " | "scan: " | "config: " | "errors: " } 
     regex collection { [<:!keyword>]* } 
} 

、その後、次のキーワードまで、すべてのデータの収集を開始します。しかし、たびに、私はちょうど "プール:" - >すべての残りのテキストを取得します。

keyword => 「pool: 」 
collection => 「homePool 
state: ONLINE 
status: Some supported features are not enabled on the pool. The pool can 
    still be used, but some features are unavailable. 
action: Enable all features using 'zpool upgrade'. Once this is done, 
    the pool may no longer be accessible by software that does not support 
    the features. See zpool-features(5) for details. 
    scan: none requested 
config: 

    NAME            STATE  READ WRITE CKSUM 
    homePool           ONLINE  0  0  0 
     mirror-0           ONLINE  0  0  0 
     ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7NUE93C  ONLINE  0  0  0 
     ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7RE2A4F  ONLINE  0  0  0 
    cache 
     ata-KINGSTON_SV300S37A60G_50026B7261025D7E-part3 ONLINE  0  0  0 

errors: No known data errors 
」 

キーワードを見つけたときに文字を食べるのをやめて、別のキーワードとして扱う方法を知りません。あなたが<:!keyword>代わりの<!keyword>を書いた

答えて

5

問題1

。それはあなたが望むものではありません。 :を削除する必要があります。

P6正規表現matches a single character with the specified Unicode property<:foo>の構文です。この場合、プロパティ:foo:foo(True)を意味します。

<:!keyword>は、Unicodeプロパティ:keyword(False)の1文字に一致します。

しかし、Unicodeプロパティ:keywordはありません。

したがって、負のアサーションは常に真であり、毎回入力の1文字に一致します。

パターンは、あなたが知っているように、テキストの残りの部分を通り抜けるだけです。あなたは、問題1を解決したら

問題2

、第二の問題が発生します。

<:!keyword>は、Unicodeプロパティ:keyword(False)の1文字に一致します。それは一致するたびに自動的に入力(1文字)を実行します。

対照的に、<!keyword>は、一致する場合は入力を消費しません。 それを使用するパターンが入力を狂わせていることを確認する必要があります。


これらの2つの問題を修正すると、期待した出力が得られます。次の問題は、入力ファイルの例のconfig::にはスペースがないため、configキーワードが機能しないことです。

my @keywords = <pool state status action scan config errors> ; 

say grammar zpool { 
    token TOP  { \s+ [ <keyword> <collection> ]* } 
    token keyword { @keywords ': ' } 
    token collection { [ <!keyword> . ]* } 
} 

私はtoken宣言にすべてのパターンを切り替えた:いくつかのクリーンアップとそう)


、。 を知っていない限り、一般的には常にtokenを使用してください。 (regexは、バックトラックを可能にします。そのことができます劇的に遅いものを下に注意しないなら。ruleは、ルール大幅にスペースします。)私は、配列にキーワードを抽出してきました

@keywordsは、@keywords[0] | @keywords[1] | ...を意味します。

私は(そうでない場合<!foo>は、任意の入力を消費しないことを考えると起こる無限ループを回避するために、入力の文字の価値を消費する)最後のパターンで<!keyword>.を追加しました。

あなたがそれらを見たことがない場合は、available grammar debugging optionsがあなたの友人であることに注意してください。

私は今、良い文法を愛し、そして、これはsplitを呼び出して解決する方がはるかに簡単ですな限り第H

3

my $input = q:to/EOF/; 
    pool: thisPool 
state: ONLINE 
status: Some supported features are not enabled on the pool. The pool can 
    still be used, but some features are unavailable. 
action: Enable all features using 'zpool upgrade'. Once this is done, 
    the pool may no longer be accessible by software that does not support 
    the features. See zpool-features(5) for details. 
    scan: none requested 
config: 

    NAME            STATE  READ WRITE CKSUM 
    homePool           ONLINE  0  0  0 
     mirror-0           ONLINE  0  0  0 
     ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7NUE93C  ONLINE  0  0  0 
     ata-WDC_WD5000AZLX-00CL5A0_WD-WCC3F7RE2A4F  ONLINE  0  0  0 
    cache 
     ata-KINGSTON_SV300S37A60G_50026B7261025D7E-part3 ONLINE  0  0  0 

errors: No known data errors 
EOF 

my @delimiter = <pool state status action scan config errors>; 
my %fields; 
for $input.split(/ ^^ \h* (@delimiter) ':' \h*/, :v)[1..*] -> $key, $value { 
    %fields{ $key[0] } = $value.trim; 
} 

say %fields.perl; 

これは捨て、知られているキーを上分割することによって動作します最初の要素(入力が値ではなくキーで始まることがわかっているので)、そしてロックステップでキーと値を繰り返します。

文法を依頼して以来、各値を.+?(可能な限り短い文字列)に置き換えることで、splitの呼び出しを純粋な正規表現に簡単に切り替えることができます。

my @delimiter = <pool state status action scan config errors>; 
grammar ZPool { 
    regex key  { @delimiter    } 
    regex keychunk { ^^ \h* <key> ':'  } 
    regex value { .*?     } 
    regex chunks { <keychunk> \h* <value> } 
    regex TOP  { <chunks>+    } 
} 

我々は、ネストされた試合の木から結果を抽出するハードワークを行う、またはその代わりにステートフルなアクションオブジェクトでカンニングできます:

class ZPool::Actions { 
    has $!last-key; 
    has %.contents; 
    method key($m) { $!last-key = $m.Str    } 
    method value($m) { %!contents{ $!last-key } = $m.trim } 
} 

そして今のはそれにいくつかのより多くの構造を与えてみましょうその後、

そして、それを使用します。

my $actions = ZPool::Actions.new; 
ZPool.parse($input, :$actions); 
say $actions.contents.perl; 

keykeychunkはバックトラックする必要がないため、regexからtokenに変更できます。

もちろん、.+?を使用してバックトラッキングすることは不正行為だと思われる可能性があるので、raifはvalue正規表現内のネガティブ先読みで言及したトリックを使用できます。