PHP,preg_replace中的嵌套模板

时间:2012-10-16 10:03:26

标签: php regex preg-replace

preg_replace("/\[b\](.*)\[\/b\]/Usi", "<strong>$1</strong>", "Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... [b]and here[/b]");

返回

Some text here... <strong>[b]Hello, [b]PHP!</strong>[/b][/b] ... <strong>and here</strong>

但我需要替换所有[b] ... [/ b]标签。为什么在我的情况下不会发生这种情况?

4 个答案:

答案 0 :(得分:3)

是的,如果元素是嵌套的,则需要多遍方法。这可以通过两种方式之一完成;从内到外或从外到内进行匹配。这里有两个经过测试的脚本,带有完全注释的正则表达式,用于说明每种技术:

1。从内到外替换:

<?php // test.php Rev:20121016_0900
$re = '% # Match innermost [b]...[/b] structure.
    \[b\]              # Literal start tag.
    (                  # $1: Element contents.
      # Use Friedls "Unrolling-the-Loop" technique:
      #   Begin: {normal* (special normal*)*} construct.
      [^[]*            # {normal*} Zero or more non-"[".
      (?:              # Begin {(special normal*)*}.
        \[             # {special} Tag open literal char,
        (?!/?b\])      # but only if NOT [b] or [/b].
        [^[]*          # More {normal*}.
      )*               # Finish {(special normal*)*}.
    )                  # $1: Element contents.
    \[/b\]             # Literal end tag.
    %x';
printf("Replace matching tags from the inside out:\n");
$text = file_get_contents('testdata.txt');
$i=0; // Keep track of iteration number.
printf("i[%d]=%s", $i++, $text);
while(preg_match($re, $text)){
    $text = preg_replace($re, '<strong>$1</strong>', $text);
    printf("i[%d]=%s", $i++, $text);
}
?>

输出:

'''
Replace matching tags from the inside out:
i[0]=Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... [b]and here[/b]
i[1]=Some text here... [b][b]Hello, <strong>PHP!</strong>[/b][/b] ... <strong>and here</strong>
i[2]=Some text here... [b]<strong>Hello, <strong>PHP!</strong></strong>[/b] ... <strong>and here</strong>
i[3]=Some text here... <strong><strong>Hello, <strong>PHP!</strong></strong></strong> ... <strong>and here</strong>
'''

2。从外面替换:

<?php // test.php Rev:20121016_0901
$re = '% # Match outermost [b]...[/b] structure.
    \[b\]              # Literal start tag.
    (                  # $1: Element contents.
      (?:              # Zero or more contents alternatives.
        [^[]*          # Either non-[b]...[/b] stuff...
        (?:            # Begin {(special normal*)*}.
          \[           # {special} Tag open literal char,
          (?!/?b\])    # but only if NOT [b] or [/b].
          [^[]*        # More {normal*}.
        )*             # Finish {(special normal*)*}.
      | (?R)           # Or a nested [b]...[/b] structure.
      )*               # Zero or more contents alternatives.
    )                  # $1: Element contents.
    \[/b\]             # Literal end tag.
    %x';
printf("Replace matching tags from the outside in:\n");
$text = file_get_contents('testdata.txt');
$i=0; // Keep track of iteration number.
printf("i[%d]=%s", $i++, $text);
while(preg_match($re, $text)){
    $text = preg_replace($re, '<strong>$1</strong>', $text);
    printf("i[%d]=%s", $i++, $text);
}
?>

输出:

'''
Replace matching tags from the outside in:
i[0]=Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... [b]and here[/b]
i[1]=Some text here... <strong>[b]Hello, [b]PHP![/b][/b]</strong> ... <strong>and here</strong>
i[2]=Some text here... <strong><strong>Hello, [b]PHP![/b]</strong></strong> ... <strong>and here</strong>
i[3]=Some text here... <strong><strong>Hello, <strong>PHP!</strong></strong></strong> ... <strong>and here</strong>
'''

请注意第二种方法中使用的(?R)递归表达式。

答案 1 :(得分:2)

它不起作用的原因:你抓住第一个[b],然后转到下一个[/ b],并保持两者之间不变。即,您更改外部[b]标签,但不更改嵌套在内部的标签。

您对@meza的评论建议您要成对替换伪标签,否则请保持不变。最好的方法是使用多次传递,比如

$markup = "Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... [b]and here[/b]";
$count = 0;
do {
    $markup = preg_replace("/\[b\](.*?)\[\/b\]/usi", "<strong>$1</strong>", $markup, -1, $count );
} while ( $count > 0 );

print $markup;

我甚至不确定你是否可以在一行正则表达式中进行,但即使你可以,它也会相当复杂,因此难以维护。

答案 2 :(得分:0)

为什么在这种特殊情况下使用正则表达式?你可以通过一个简单的字符串来取代每个[b]到强,每个[/ b]替换为/ strong。

答案 3 :(得分:0)

修改修饰符Usi并将其替换为sim

修改

试一试:

<?php

   function matchReplaceAll($reg, $replace, $str)
    {
        while (preg_match($reg, $str))
        {
            $str = preg_replace($reg, $replace, $str);
        }
        return $str;
    }



$str="Some text here... [b][b]Hello, [b]PHP![/b][/b][/b] ... and here";
$str=matchReplaceAll('/\[b\](.*?)\[\/b\]/sim', '<strong>$1</strong>', $str);
echo $str;