例如,我有一个这样的字符串:
{% a %}
{% b %}
{% end %}
{% end %}
我想获取{% a %}
和{% end %}
之间的内容,即{% b %} {% end %}
。
我曾经使用{%\ S +%}(。*){%end%}来执行此操作。但是当我在其中添加c时:
{% a %}
{% b %}
{% end %}
{% end %}
{% c %}
{% end %}
它不起作用......我怎么能用正则表达式做到这一点?
答案 0 :(得分:4)
鉴于此测试数据:
$text = '
{% a %}
{% b %}
{% a %}
{% end %}
{% end %}
{% b %}
{% end %}
{% end %}
{% c %}
{% end %}
';
这个经过测试的脚本可以解决问题:
<?php
$re = '/
# Match nested {% a %}{% b %}...{% end %}{% end %} structures.
\{%[ ]\w[ ]%\} # Opening delimiter.
(?: # Group for contents alternatives.
(?R) # Either a nested recursive component,
| # or non-recursive component stuff.
[^{]*+ # {normal*} Zero or more non-{
(?: # Begin: "unrolling-the-loop"
\{ # {special} Allow a { as long
(?! # as it is not the start of
%[ ]\w[ ]%\} # a new nested component, or
| %[ ]end[ ]%\} # the end of this component.
) # Ok to match { followed by
[^{]*+ # more {normal*}. (See: MRE3!)
)*+ # End {(special normal*)*} construct.
)*+ # Zero or more contents alternatives
\{%[ ]end[ ]%\} # Closing delimiter.
/ix';
$count = preg_match_all($re, $text, $m);
if ($count) {
printf("%d Matches:\n", $count);
for ($i = 0; $i < $count; ++$i) {
printf("\nMatch %d:\n%s\n", $i + 1, $m[0][$i]);
}
}
?>
这是输出:
2 Matches:
Match 1:
{% a %}
{% b %}
{% a %}
{% end %}
{% end %}
{% b %}
{% end %}
{% end %}
Match 2:
{% c %}
{% end %}
修改:如果您需要匹配包含多个字词char的开头标记,请将\w
代币的两次出现替换为(?!end)\w++
,(正确无误)在tchrist的优秀答案中实现。)
答案 1 :(得分:2)
以下是Perl中一个适用于您的数据集的方法的演示。这同样适用于PHP。
#!/usr/bin/env perl
use strict;
use warnings;
my $string = <<'EO_STRING';
{% a %}
{% b %}
{% end %}
{% end %}
{% c %}
{% end %}
EO_STRING
print "MATCH: $&\n" while $string =~ m{
\{ % \s+ (?!end) \w+ \s+ % \}
(?: (?: (?! % \} | % \} ) . ) | (?R) )*
\{ % \s+ end \s+ % \}
}xsg;
运行时,产生这个:
MATCH: {% a %}
{% b %}
{% end %}
{% end %}
MATCH: {% c %}
{% end %}
还有其他几种方法可以写出来。您可能还有其他未显示的约束,但这应该可以帮助您入门。
答案 2 :(得分:0)
您要找的是recursive regex。 PHP使用(?R)
支持它。
我对它本身并不熟悉,能够帮助你解决模式本身,但希望这是朝着正确的方向发展。