1

次のような構文を使用する基本的なテキスト テンプレート エンジンがあります。

foo bar
%IF MY_VAR
  some text
  %IF OTHER_VAR
    some other text
  %ENDIF
%ENDIF
bar foo

解析に使用している正規表現に問題があり、ネストされた IF/ENDIF ブロックが考慮されていません。

私が使用している現在の正規表現は次のとおりです。%IF (?<Name>[\w_]+)(?<Contents>.*?)%ENDIF

これが.NETで「再帰的」正規表現をサポートする推奨される方法であることを理解しているため、キャプチャグループのバランス調整(.NETの正規表現ライブラリの機能)について読んでいます。

私はグループのバランスをとって遊んでいて、これまでのところ次のことを思いつきました:

(
 (
  (?'Open'%IF\s(?<Name>[\w_]+))
  (?<Contents>.*?)
 )+
 (
  (?'Close-Open'%ENDIF)(?<Remainder>.*?)
 )+
)*
(?(Open)(?!))

しかし、これは私が期待するように完全に動作しているわけではありません。たとえば、多くの空のグループをキャプチャしています。ヘルプ?

4

1 に答える 1

5

バランスの取れた IF ステートメントで IF/ENDIF ブロック全体をキャプチャするには、次の正規表現を使用できます。

%IF\s+(?<Name>\w+)
(?<Contents>
    (?> #Possessive group, so . will not match IF/ENDIF
        \s|
        (?<IF>%IF)|     #for IF, push
        (?<-IF>%ENDIF)| #for ENDIF, pop
        . # or, anything else, but don't allow
    )+
    (?(IF)(?!)) #fail on extra open IFs
)   #/Contents
%ENDIF

ここでのポイントは次のとおりです。すべての名前付きグループを 1 つ以上キャプチャすることはできません。たとえば、最後にキャプチャされた値のMatch1 つのグループのみが取得されます。(?<Name>\w+)私の正規表現では、単純な正規表現のNameandContentsグループを保持し、グループ内のバランスを制限しました。正規表現はまだand でContentsラップされています。IFENDIF

データがより複雑になると興味深いものになります。例えば:

%IF MY_VAR             
  some text
  %IF OTHER_VAR
    some other text
  %ENDIF
  %IF OTHER_VAR2
    some other text 2
  %ENDIF
%ENDIF                 
%IF OTHER_VAR3         
    some other text 3
%ENDIF                 

MY_VARここでは、との 2 つの一致が得られますOTHER_VAR3MY_VARのコンテンツの2 つの ifs をキャプチャする場合は、そのContentsグループで正規表現を再実行する必要があります (必要に応じて先読みを使用して回避できます。正規表現全体を でラップし(?=...)ますが、それを配置する必要があります)。位置と長さを使用して、何らかの形で論理構造に変換します)。

さて、基本的なことは理解できたようなので、あまり説明しませんが、contents グループについて簡単に説明します。後戻りを避けるために、所有格グループを使用しています。そうしないと、ドットが最終的にIFs 全体に一致し、バランスが崩れる可能性があります。グループの遅延一致は、(( )+?の代わりに(?> )+) 同様に動作します。

于 2010-11-26T15:04:55.553 に答える