あなたの質問の前提は、特定のパターンを一致させ、一致したテキストに対して追加の処理を実行した後にそれを置き換えることです。
の理想的な候補のようですpreg_replace_callback
一致した括弧、引用符、中括弧などをキャプチャするための正規表現は非常に複雑になる可能性があり、正規表現ですべてを行うことは実際には非常に非効率的です。実際、それが必要な場合は、適切なパーサーを作成する必要があります。
この質問では、限られたレベルの複雑さを想定し、正規表現を使用した2段階の解析でそれに取り組みます。
まず第一に、中括弧の間のトークンをキャプチャするために私が考えることができる最も単純な正規表現。
/{([^}]+)}/
それを分解しましょう。
{ # A literal opening brace
( # Begin capture
[^}]+ # Everything that's not a closing brace (one or more times)
) # End capture
} # Literal closing brace
結果のある文字列に適用すると、preg_match_all
次のようになります。
array (
0 => array (
0 => 'A string {TOK_ONE}',
1 => ' with {TOK_TWO|0=>"no", 1=>"one", 2=>"two"}',
),
1 => array (
0 => 'TOK_ONE',
1 => 'TOK_TWO|0=>"no", 1=>"one", 2=>"two"',
),
)
これまでのところよさそうだ。
文字列に中括弧がネストされている場合、つまり{TOK_TWO|0=>"hi {x} y"}
、この正規表現は機能しないことに注意してください。これが問題にならない場合は、次のセクションにスキップしてください。
トップレベルのマッチングを行うことは可能ですが、私がこれまでに行うことができた唯一の方法は、再帰を使用することです。ほとんどの正規表現のベテランは、正規表現に再帰を追加するとすぐに、正規表現ではなくなると言うでしょう。
これは、追加の処理の複雑さが始まる場所であり、長く複雑な文字列を使用すると、スタックスペースを使い果たしてプログラムをクラッシュさせるのは非常に簡単です。使用する必要がある場合は、慎重に使用してください。
私の他の回答の1つから取得し、少し変更した再帰正規表現。
`/{((?:[^{}]*|(?R))*)}/`
故障した。
{ # literal brace
( # begin capture
(?: # don't create another capture set
[^{}]* # everything not a brace
|(?R) # OR recurse
)* # none or more times
) # end capture
} # literal brace
そして今回の出力はトップレベルの中括弧にのみ一致します
array (
0 => array (
0 => '{TOK_ONE|0=>"a {nested} brace"}',
),
1 => array (
0 => 'TOK_ONE|0=>"a {nested} brace"',
),
)
繰り返しますが、必要がない限り、再帰的な正規表現を使用しないでください。(古いPCREライブラリがある場合、システムはそれらをサポートしない可能性があります)
それが邪魔にならないように、トークンにオプションが関連付けられているかどうかを確認する必要があります。質問のように2つのフラグメントを照合する代わりに、私の例のようにトークンを使用してオプションを保持することをお勧めします。{TOKEN|0=>"option"}
一致したトークンが含まれていると仮定$match
し、パイプをチェックし、|
その後にすべてのサブストリングを取得すると、オプションのリストが残ります。ここでも、正規表現を使用してそれらを解析できます。(最後にすべてをまとめますのでご安心ください)
/(\d)+\s*=>\s*"([^"]*)",?/
故障した。
(\d)+ # Capture one or more decimal digits
\s* # Any amount of whitespace (allows you to do 0 => "")
=> # Literal pointy arrow
\s* # Any amount of whitespace
" # Literal quote
([^"]*) # Capture anything that isn't a quote
" # Literal quote
,? # Maybe followed by a comma
そして、例の一致
array (
0 => array (
0 => '0=>"no",',
1 => '1 => "one",',
2 => '2=>"two"',
),
1 => array (
0 => '0',
1 => '1',
2 => '2',
),
2 => array (
0 => 'no',
1 => 'one',
2 => 'two',
),
)
引用符の中で引用符を使用する場合は、独自の再帰正規表現を作成する必要があります。
まとめ、これが実際の例です。
いくつかの初期化コード。
$options = array(
'WERE' => 1,
'TYPE' => 'cat',
'PLURAL' => 1,
'NAME' => 2
);
$string = 'There {WERE|0=>"was a",1=>"were"} ' .
'{TYPE}{PLURAL|1=>"s"} named bob' .
'{NAME|1=>" and bib",2=>" and alice"}';
そしてすべて一緒に。
$string = preg_replace_callback('/{([^}]+)}/', function($match) use ($options) {
$match = $match[1];
if (false !== $pipe = strpos($match, '|')) {
$tokens = substr($match, $pipe + 1);
$match = substr($match, 0, $pipe);
} else {
$tokens = array();
}
if (isset($options[$match])) {
if ($tokens) {
preg_match_all('/(\d)+\s*=>\s*"([^"]*)",?/', $tokens, $tokens);
$tokens = array_combine($tokens[1], $tokens[2]);
return $tokens[$options[$match]];
}
return $options[$match];
}
return '';
}, $string);
エラーチェックは最小限であることに注意してください。存在しないオプションを選択すると、予期しない結果が発生します。
これらすべてを行うにはおそらくもっと簡単な方法がありますが、私はそのアイデアを採用して実行しました。