你提这个问题的前提是你想match一个具体的pattern进而replace表演后附加处理在匹配的文本上。
似乎是一个理想的候选人preg_replace_callback http://php.net/preg_replace_callback
用于捕获匹配的括号、引号、大括号等的正则表达式可能会变得非常复杂,并且使用正则表达式来完成这一切实际上效率相当低。事实上,如果您需要的话,您需要编写一个适当的解析器。
对于这个问题,我将假设有限的复杂性,并使用正则表达式通过两阶段解析来解决它。
首先,我能想到的最简单的正则表达式用于捕获大括号之间的标记。
/{([^}]+)}/
让我们来分解一下。
{ # 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"}
,这个正则表达式将不起作用。如果这不是问题,请跳至下一部分。
可以进行顶级匹配,但我能够做到这一点的唯一方法是通过递归。大多数正则表达式老手都会告诉您,一旦您向正则表达式添加递归,它就不再是正则表达式了。
这就是额外的处理复杂性开始出现的地方,对于长而复杂的字符串,很容易耗尽堆栈空间并使程序崩溃。如果您确实需要使用它,请小心使用。
递归正则表达式取自我的其他答案之一 https://stackoverflow.com/a/11468459/599857并做了一些修改。
`/{((?:[^{}]*|(?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 库,甚至可能不支持它们)
有了这个,我们需要弄清楚令牌是否有与之关联的选项。我建议不要根据您的问题匹配两个片段,而是根据我的示例保留带有标记的选项。{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);
请注意,错误检查是最少的,如果您选择不存在的选项,将会出现意外的结果。
可能有更简单的方法来完成所有这一切,但我只是接受了这个想法并付诸实践。