关于字符串替换及 preg_replace 函数的后向引用

关于字符串替换及 preg_replace 函数的后向引用

在 PHP 里面替换字符串有很多种方法,str_replace 是再常见不过了,复杂一点的也可能会用到 preg_replace 方法。

这两个方法(str_replace 和 preg_replace)除了正则外,在循环替换的问题上也有一个恶心的差异。先看看各自的语法说明:

str_replace

mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

preg_replace

mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

很明显在可选的参数列表中,preg_replace 多了个 limit 项,这个 limit 项控制了字符串替换操作的次数。

那么问题来了,现在需要实现如下一个需求,将下列代码中的两个 {module} 分别替换成不同的字符串:

<div >

<div >

<div >{module}</div>

</div>

<div >{module}</div>

</div>

如果用 str_replace 方法,一次替换操作无法实现只替换一个 {module} 字符串。

如果用 preg_replace 方法,发现这个会造成个要命的 bug,比如下面这段代码:

<?php

$replace = '$12.34';

$subject = 'Pay {replace} for it.';

echo preg_replace('/\{replace\}/', $replace, $subject);

?>

输出的结果是:

Pay .34 for it.

而不是期望的:

Pay $12.34 for it.

究其原因,是因为 preg_replace 的第二参数存在后向引用的问题:

replacement中可以包含后向引用\\n 或(php 4.0.4以上可用)$n,语法上首选后者。 每个 这样的引用将被匹配到的第n个捕获子组捕获到的文本替换。 n 可以是0-99,\\0和$0代表完整的模式匹配文本。 捕获子组的序号计数方式为:代表捕获子组的左括号从左到右, 从1开始数。如果要在replacement 中使用反斜线,必须使用4个(“\\\\”,译注:因为这首先是php的字符串,经过转义后,是两个,再经过 正则表达式引擎后才被认为是一个原文反斜线)。

要避免这个问题,有两个解决方案:

第一种,$ 符号使用实体字符($),避免造成和 PHP 语言的冲突。

第二种,使用 strpos 结合 substr_replace 的方法。例如:

$start = strpos($str, '{module}');

$str = substr_replace($str, 'http://www.mangguo.org', $start, sizeof('{module}'));

参考资料:

[1] http://php.net/manual/zh/function.str-replace.php

[2] http://php.net/manual/zh/function.preg-replace.php