如何使用 preg_replace_callback?

42

我有以下的 HTML 语句

[otsection]Wallpapers[/otsection]
WALLPAPERS GO HERE

[otsection]Videos[/otsection]
VIDEOS GO HERE

我想要做的是将[otsection]标记替换为html div。但是有一个问题,我希望将div的id从1递增为2,再递增为3,以此类推。

因此,举个例子,上述语句应该被翻译为

<div class="otsection" id="1">Wallpapers</div>
WALLPAPERS GO HERE

<div class="otsection" id="2">Videos</div>
VIDEOS GO HERE
据我所知,做到这一点的最佳方法是使用preg_replace_callback函数来在每个替换之间递增id变量。但是,在花费1小时尝试后,我无法让它正常工作。 非常感谢任何对此提供帮助的人!
2个回答

55

请使用以下代码:

$out = preg_replace_callback(
    "(\[otsection\](.*?)\[/otsection\])is",
    function($m) {
        static $id = 0;
        $id++;
        return "<div class=\"otsection\" id=\"ots".$id."\">".$m[1]."</div>";
    },
    $in);

特别要提醒的是,我使用了一个 static 变量。这个变量会在函数调用之间保留,也就是说每次调用函数时都会递增它的值,而这将会发生在每个匹配中。

另外,请注意我在 ID 前面加了一个 ots。元素的 ID 不应该以数字开头。


对于 PHP 5.3 之前的版本:

$out = preg_replace_callback(
    "(\[otsection\](.*?)\[/otsection\])is",
    create_function('$m','
        static $id = 0;
        $id++;
        return "<div class=\"otsection\" id=\"ots".$id."\">".$m[1]."</div>";
    '),
    $in);

@Kolink:为什么你在代码的第二行使用了“is”这个词?我是新手,请帮忙解释一下。 - Neetz
i 是一个标志,使其不区分大小写。 s 允许 . 匹配换行符。 - Niet the Dark Absol
请不要再使用无用的<code>create_function()</code>函数了!请注意:警告该函数内部执行eval(),因此具有与eval()相同的安全问题。此外,它具有糟糕的性能和内存使用特性。文档 - Michael Probst
2
@MichaelProbst 这就是为什么你应该升级到支持匿名函数的PHP版本(5.3或更高版本)。如果你仍在使用5.2,那么你面临的问题比一个伪装的eval更大;) - Niet the Dark Absol
@NiettheDarkAbsol:我同意出于安全原因应该使用PHP 5.3+,但即使使用5.3+,如果您使用function_create()而不是匿名函数,仍然会存在eval()问题。而上面的示例显示了function_create(),这就是为什么我要发出警告的原因。 - Michael Probst
显示剩余6条评论

37

注意:以下内容旨在提供一般性答案,并不尝试解决OP的特定问题,因为此问题已经被解决

preg_replace_callback()是什么?

这个函数用于执行正则表达式的搜索和替换。它类似于str_replace(),但它不是简单地进行字符串替换,而是根据用户定义的正则表达式模式搜索,然后对匹配项应用回调函数。如果找到了匹配项,则函数返回修改后的字符串,否则返回原始字符串。

何时使用它?

preg_replace_callback()preg_replace()非常相似,唯一的区别是,在第二个参数中指定一个回调函数来替换字符串,而不是指定要替换的字符串。

当您想要进行简单的正则表达式搜索和替换时,请使用preg_replace()。当您想要做更多替换操作时,请使用preg_replace_callback()。请参阅下面的示例,以了解它的工作原理。

如何使用它?

下面是一个示例,用于说明该函数的用法。在这里,我们正在尝试将日期字符串从YYYY-MM-DD格式转换为DD-MM-YYYY

// our date string
$string  = '2014-02-22';

// search pattern
$pattern = '~(\d{4})-(\d{2})-(\d{2})~';

// the function call
$result = preg_replace_callback($pattern, 'callback', $string);

// the callback function
function callback ($matches) {
    print_r($matches);
    return $matches[3].'-'.$matches[2].'-'.$matches[1];
}

echo $result;
在这里,我们的正则表达式模式搜索格式为NNNN-NN-NN的日期字符串,其中N可以是0-9之间的数字(\d是字符类[0-9]的速记表示)。回调函数将被调用并传递给定字符串中匹配元素的数组。
最终结果将是:
22-02-2014

注:上述示例仅用于说明目的。您不应该使用它来解析日期。请改用DateTime::createFromFormat()DateTime::format()。有关更多详细信息,请参见此问题


1
一件事情,一旦明白了就很显而易见,但如果你刚开始使用preg_函数,可能需要注意的是,在示例中 $matches 从1开始而不是0,因为0包含了匹配的整个字符串。参见https://stackoverflow.com/a/14383726/957246。 - trapper_hag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接