0

私はこのようなものを解析したい:

Hi [{tagname:content}] [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag
[{tag2: more data here}] kj udf}]

このPCRE正規表現を使用して、その間のすべてのデータを取得していますが\[{(.*?)}\]、ネストされたタグでは機能しません。私はPCREの専門家ではありません。

4

4 に答える 4

4

PCREは、Perlと同様に、ネストされた構造を任意の深さに一致させることができます。テストされたスクリプトは次のとおりです。

ネストされた角かっこに一致する正規表現

<?php // test.php Rev:20120701_0800
$re_nested_double_bracket ='% # Rev:20120701_0800
# Match [{...[{...}]...}] structure with arbitrary nesting.
\[\{                      # Opening literal double bracket.
(                         # $1: Contents of double brackets.
  (?:                     # Group for contents alternatives.
    [^\[\}]++             # Either one or more non-brackets,
  | (?R)                  # or a nested bracket pair,
  | \[                    # or the start of opening bracket
    (?!\{)                # (if not a complete open bracket),
  | \}                    # or the start of closing bracket
    (?!\])                # (if not a complete close bracket).
  )*                      # Zero or more contents alternatives.
)                         # End $1: Contents of double brackets.
\}\]                      # Closing literal double bracket.
%x';

$input = file_get_contents('testdata.txt');
$count = preg_match_all($re_nested_double_bracket, $input, $matches);
printf("There were %d matches found.\n", $count);
for ($i = 0; $i < $count; ++$i) {
    printf("  Match[%d]: %s\n", $i + 1,  $matches[0][$i]);
}
?>

元の投稿のテストデータに対して実行すると、正規表現が一致するものは次のようになります。

出力例:

There were 2 matches found.
Match[1]: [{tagname:content}]
Match[2]: [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag
[{tag2: more data here}] kj udf}]

この正規表現は、ネストされている可能性のある角かっこの最も外側$1のセットと一致し、角かっこの間の内容をグループにまとめることに注意してください。ネストされた角かっこを解析する場合は、一致するものがなくなるまで、最も外側の角かっこの内容に対して正規表現を再帰的に再実行する必要があります。

最新の正規表現エンジン(つまり、Perl、PCRE / PHP、.NET)がネストされた構造を解析できないと主張する人は、単に間違っています。正規表現は長い間「REGULAR」ではありませんでした...

編集:2012-07-01 09:00このソリューションは、ネストされた角かっこを任意の「任意の深さ」に一致させますが、システムメモリ、実行可能スタックサイズとPHP pcre.backtrack_limitpcre.recursion_limitおよびmemory_limit構成変数によって常に制限されることに注意してください。サブジェクト文字列が大きすぎる場合や、特定のホストシステムに対してネストが深すぎる場合、この正規表現ソリューションが失敗する可能性があることに注意してください。PHP / PCREライブラリによって、実行中の実行可能ファイルがスタックオーバーフロー、セグメンテーション違反、およびプログラムクラッシュを生成する可能性もあります。これが発生する方法と理由(およびそれを回避し、この種のエラーを適切に処理する方法)の詳細については、関連する質問への回答を参照してください。 ブラウザエラーを返すpreg_match関数のRegExpPHP正規表現:このコードに何か問題がありますか?

于 2012-07-01T14:08:40.877 に答える
3

これは、REGEXでよくある問題です。バックリーが言うように、彼らはこれのために設計されていませんでした。それにもかかわらず、問題はたくさん発生します。

基本的な問題は、ネストされたタグの閉じ括弧が実際には外側のタグの閉じ括弧ではないことをREGEXが知る方法がないことです。

私はいくつかの破壊行為をすることに決め、この恐怖を思いついた。コンセプトは、最初に他のタグを含まないタグを引き出すことです。次に、すべてのタグが含まれるまで外向きに機能します。

$str = "Hi [{tagname:content}] [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag [{tag2: more data here}] kj udf}]";
$matches = array();
function replace_cb($this_match) {
    global $matches;
    $this_match = $this_match[0];
    foreach($matches as $index => $match) $this_match = str_replace('**'.($index + 1).'**', $match, $this_match);
    array_push($matches, $this_match);
    return '**'.count($matches).'**';
}
while(preg_match('/\[\{[^\[]*?\}\]/', $str)) $str = preg_replace_callback('/\[\{[^\[]*?\}\]/', 'replace_cb', $str);
print_r($matches);

出力:

Array
(
    [0] => [{tagname:content}]
    [1] => [{tag2: more data here}]
    [2] => [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag [{tag2: more data here}] kj udf}]
)

...したがって、3つのタグすべてが分離されてしまいます。

弱点の1つは、タグにが含まれている場合、タグにネストされたタグが含まれていると現在判断していること[です。これは[{一緒にする必要がありますが、REGEXのサブ文字列を無効にすることはできず、文字または文字の範囲のみを無効にすることはできないため、これは困難です。

だから、とても恐ろしい。しかし、それは機能します:)

于 2012-07-01T12:19:33.153 に答える
2

無制限のネストを可能にするために正規表現を使用する場合、一般的な解決策はありません。彼らはそのために作られていませんでした。

以下は、[{および}]で区切られたコメントと一致し、1レベルのネストされたコメントを内部に許可します。。*?の代わりに負の先読みが使用されます。件名の文字列に不均衡な[{文字が含まれている場合に、壊滅的なバックトラックを防ぐため。

\[{(?:(?!}]|\[{).)*+(?:\[{(?:(?!}]|\[{).)*+}](?:(?!}]|\[{).)*+)*+.*?}]
于 2012-07-01T11:41:43.943 に答える
2

正規表現はパーサーではありません。

軽量のソリューションの場合、JSONパーサーを使用することをお勧めします。たとえば、次のようになります。

$tree = json_decode('["root","'.
                    preg_replace('/\[\{(\w+):/',
                                 '",["\1","',
                                 str_replace(array('\\',   "\n", '"',  '}]'),
                                             array('\\\\', '\n', '\"', '"],"'),
                                             $str).
                    '"]'));

この入力(あなたの例)の場合:

$str = 'Hi [{tagname:content}] [{tag1:xnkudfdhkfujhkdjki diidfo now nested tag
[{tag2: more data here}] kj udf}]';

次の出力が得られます。

$tree = array(
   0 => "root",
   1 => "Hi ",
   2 => array(
      0 => "tagname",
      1 => "content"
   ),
   3 => " ",
   4 => array(
      0 => "tag1",
      1 => "xnkudfdhkfujhkdjki diidfo now nested tag\n",
      2 => array(
         0 => "tag2",
         1 => " more data here"
      ),
      3 => " kj udf"
   ),
   5 => ""
);

タグ名は0各サブツリーの要素です(任意の"root"タグを追加しました)。タグ名は単純なものと仮定しました\w+。許可されたタグ名を反映するように変更する必要があります。ご覧のとおり、解析ツリーに余分な空の文字列がある可能性がありますが、それらを簡単に取り除くことができます。

あなたの質問はPCREについてでしたが、それはネジを回すための正しいハンマーを求めるようなものでした。

ところで、再帰的な正規表現エンジンで構築されたパーサーには、理論的には非常に現実的な欠点が1つあります。ツリー内の深さの回数だけすべての入力要素を再スキャンする必要があるため、バックトラックがないと仮定した場合の最悪の場合の時間計算量はO(n 2)。

于 2012-07-01T12:28:44.997 に答える