Perlでは、文字列のレキシカル分析(レキシング)とパースは、複雑なテキストテンプレートの処理によく使用されます。多くの問題(例えば、ネストされたカッコ、引用符、SQLクエリの解析など)は、その線形的な性質のために単純な正規表現では対応できません。そのため、パースの基本を実装したモジュール、例えばText::Balancedを使用します。
Text::Balancedは、バランスの取れたカッコ、対になった引用符、その他のネストされた構造を抽出するために設計されています。正規表現が無力な場所(例えば、ネストされた構造のパース{ ... { ... } ... })で機能します。代替手段としては、Parse::RecDescentモジュール、スタックや再帰に基づく独自のコード、あとはサードパーティのパーサーなどがあります。
Text::Balancedの使用例とregexpとの比較:
use Text::Balanced 'extract_bracketed'; my $data = 'foo({bar(baz)},qux)'; my ($extracted, $remainder) = extract_bracketed($data, '()'); print $extracted; # 出力: ({bar(baz)},qux)
正規表現ではネストされたカッコを正しく解析できません:
$data =~ /(\(.*\))/; # 最初と最後のカッコしかキャッチできず、ネストを無視します
一般的なPerlの正規表現を使用して、任意のネストのストリング内でバランスの取れたカッコを正しく特定することは可能ですか?
回答: いいえ、Perlの正規表現は再帰的なテンプレートを処理できません(PCREは別ですが、標準のPerlでは無理です)。このような問題に対処するには、パーサー(Text::Balanced、スタックベースのパーサー、Parse::RecDescent)を使用する必要があります。正規表現での解決を試みると、ネストされた構文で誤りが発生します。
例:
# foo({bar(baz)},qux)には機能しません my ($br) = $data =~ /(\(.*\))/;
一つの物語
プロジェクトでは、JSONを正規表現で手動で解析しようとしました。開発者は、
(\{.*\})の式が必要な部分を見つけると考えていましたが、ネストされたオブジェクトを持つ実際のデータでは、パーサーが誤った境界を選択し、データの損失と入力パラメータの処理エラーを引き起こしました。
一つの物語
XMLイベントログで、潜在的にネストされたタグを含むタグの内容を抽出する必要がありました。レキシングにおける再帰の原理を十分に理解していなかったため、イベントの解析が間違って行われ、ネストされた要素が無視され、情報の一部が失われました。
一つの物語
マイグレーションスクリプトによるSQLクエリの解析時のエラー: 丸括弧の中のサブクエリのような特異なケースを解析できませんでした。正規表現は単純なネストされた文字列の段階でも「壊れ」、不正なSQLクエリが生成されました。