PHP - 不贪婪的正则表达式还有点'greedy'
PHP - ungreedy regular expression still a little 'greedy'
对于我的 CMS,我需要替换 [?][/?] 标签之间的多行内容,如果它包含字符串 %empty%,如果没有找到 %empty% 标记则保持不变。
$a='
[?]<h1>%empty%</h1>
<p>text</p>
[/?]
text
[?]<h1>%empty%</h1>
<p>text</p>
[/?]
text';
$r= preg_replace (
'/(\[\?\]).*?%empty%.*?(\[\/\?\])/s',
"REPLACED",
$a ) ;
echo $r;
正确结果:
REPLACED
text
REPLACED
text
它几乎适用于所有组合,除非 first 行不匹配。在这种情况下,将替换第一个 [?] 和最后一个 [/?]
之间的所有内容
$a='
[?]<h1>%!empty%</h1>
<p>text</p>
[/?]
text
[?]<h1>%empty%</h1>
<p>text</p>
[/?]
text';
Wrong result:
REPLACED
text
Expected:
[?]
<h1>%!empty%</h1>
<p>test</p>
[/?]
text
REPLACED
text
我同时使用了非贪婪异常和 'lazy' 常规异常,结果相同。我想我需要在正则表达式中明确定义第二个 [/?],但没有成功。
对于您当前的示例数据,如果您要匹配从 [?]
到 [/?]
之间不能有 [?]
而必须有 %empty%
,你可以使用 .
使用 /s
修饰符使点匹配换行符:
\[\?\](?:(?!\[/?\?\]).)*%empty%(?:(?!\[\?\]).)*\[/\?]
说明
\[\?\]
匹配 [?]
(?:
非捕获组
(?!\[/?\?\]).
断言右边的不是[?]
或[/?]
。然后匹配任何字符。
)*
关闭非捕获组并重复0+次
%empty%
字面匹配
(?:
非捕获组
(?!\[\?\]).
断言直接右边的不是[?]
。然后匹配任何字符。
)*
关闭非捕获组并重复0+次
\[/\?]
匹配 [/?]
编辑
@Casimir et Hippolyte suggests a more performant pattern using a Unrolled Star Alternation Solution方法:
\[\?\][^[%]*+(?:\[(?!\?])[^[%]*|%(?!empty%)[^[{%]*)*+%empty%[^[]*+(?:\[(?!/?\?])[^[]*)*+\[/\?]
说明
\[\?\]
匹配 [?]
[^[%]*+
否定字符 class,匹配除 [
]
%
之外的任何字符
(?:
非捕获组
\[(?!\?])
匹配[
,断言直接右边的不是?]
[^[%]*
如果是这种情况,匹配 0+ 次除 [
%
之外的任何字符
|
或
%(?!empty%)
匹配%
,断言直接右边的不是empty%
[^[{%]*
如果是这种情况,匹配 0+ 次除 [
{
之外的任何字符
)*+
关闭非捕获组并使用 possessive quantifier 重复 0+ 次
%empty%[^[]*+
匹配 %empty% 和 1+ 次除 [
]
之外的任何字符
(?:
非捕获组
\[(?!/?\?])
匹配[
,断言直接右边的不是可选的/
和?]
[^[]*
如果是这种情况,匹配 0+ 次除 [
之外的任何字符
)*+
关闭非捕获组并重复0+次
\[/\?]
匹配 [/?]
对于我的 CMS,我需要替换 [?][/?] 标签之间的多行内容,如果它包含字符串 %empty%,如果没有找到 %empty% 标记则保持不变。
$a='
[?]<h1>%empty%</h1>
<p>text</p>
[/?]
text
[?]<h1>%empty%</h1>
<p>text</p>
[/?]
text';
$r= preg_replace (
'/(\[\?\]).*?%empty%.*?(\[\/\?\])/s',
"REPLACED",
$a ) ;
echo $r;
正确结果:
REPLACED
text
REPLACED
text
它几乎适用于所有组合,除非 first 行不匹配。在这种情况下,将替换第一个 [?] 和最后一个 [/?]
之间的所有内容$a='
[?]<h1>%!empty%</h1>
<p>text</p>
[/?]
text
[?]<h1>%empty%</h1>
<p>text</p>
[/?]
text';
Wrong result:
REPLACED
text
Expected:
[?]
<h1>%!empty%</h1>
<p>test</p>
[/?]
text
REPLACED
text
我同时使用了非贪婪异常和 'lazy' 常规异常,结果相同。我想我需要在正则表达式中明确定义第二个 [/?],但没有成功。
对于您当前的示例数据,如果您要匹配从 [?]
到 [/?]
之间不能有 [?]
而必须有 %empty%
,你可以使用
使用 /s
修饰符使点匹配换行符:
\[\?\](?:(?!\[/?\?\]).)*%empty%(?:(?!\[\?\]).)*\[/\?]
说明
\[\?\]
匹配[?]
(?:
非捕获组(?!\[/?\?\]).
断言右边的不是[?]
或[/?]
。然后匹配任何字符。
)*
关闭非捕获组并重复0+次%empty%
字面匹配(?:
非捕获组(?!\[\?\]).
断言直接右边的不是[?]
。然后匹配任何字符。
)*
关闭非捕获组并重复0+次\[/\?]
匹配[/?]
编辑
@Casimir et Hippolyte suggests a more performant pattern using a Unrolled Star Alternation Solution方法:
\[\?\][^[%]*+(?:\[(?!\?])[^[%]*|%(?!empty%)[^[{%]*)*+%empty%[^[]*+(?:\[(?!/?\?])[^[]*)*+\[/\?]
说明
\[\?\]
匹配[?]
[^[%]*+
否定字符 class,匹配除[
]
%
之外的任何字符
(?:
非捕获组\[(?!\?])
匹配[
,断言直接右边的不是?]
[^[%]*
如果是这种情况,匹配 0+ 次除[
%
之外的任何字符
|
或%(?!empty%)
匹配%
,断言直接右边的不是empty%
[^[{%]*
如果是这种情况,匹配 0+ 次除[
{
之外的任何字符
)*+
关闭非捕获组并使用 possessive quantifier 重复 0+ 次
%empty%[^[]*+
匹配 %empty% 和 1+ 次除[
]
之外的任何字符
(?:
非捕获组\[(?!/?\?])
匹配[
,断言直接右边的不是可选的/
和?]
[^[]*
如果是这种情况,匹配 0+ 次除[
之外的任何字符
)*+
关闭非捕获组并重复0+次\[/\?]
匹配[/?]