str_replace 能一次性替换多个吗?把我自己问住的两个问题讲清楚

作者: admin 分类: 运营、维护日志 发布时间: 2026-10-10 21:37

上一篇写”十几行代码修复评论头像裂图”的时候,我盯着那行 str_replace 冒出两个疑问,顺手查了 PHP 官方文档,发现这两个问题恰好是很多初学者会卡住的地方。既然查都查了,索性单独写一篇讲清楚。

问题是这样两个:

一、如果字符串里同一个词出现了好几次,str_replace 能一次性全部替换掉吗?
二、如果字符串里同时出现了两个不同的词,能一次性都替换成同一个目标吗?

多个相同文本块被统一替换
一个函数,两个规则,一个坑

先给答案:两个都能

但不是”碰巧能”,而是这个函数本来就是这么设计的。PHP 官方文档里两句话把这事儿说死了:

  • “returns a string with all occurrences of search in subject replaced”——替换全部出现的位置,不是只替换第一个;
  • “If search is an array and replace is a string, then this replacement string is used for every value of search”——搜索项是数组、替换项是字符串时,数组里每一项都换成同一个字符串。

规则一:同一串出现几次,就换几次

看个最小的例子:

$text = "0.gravatar.com 和 0.gravatar.com 和 0.gravatar.com";
$new  = str_replace('0.gravatar.com', 'cravatar.cn', $text, $count);

// $new   = "cravatar.cn 和 cravatar.cn 和 cravatar.cn"
// $count = 3

三个全换掉了。这里还顺带用到了 str_replace 的第四个参数 $count:它会记录一共替换了几处。这个参数平时用得少,但调试时特别好用——不确定有没有替换成功,打印一下 $count 就知道了,比肉眼找快得多。

规则二:数组里几个词,就全部换成同一个目标

当第一个参数是数组、第二个参数是单个字符串时,意思就是”这些词我都要,全给我换成这个”:

$avatar = '<img src="https://0.gravatar.com/avatar/abc?s=96"
           srcset="https://2.gravatar.com/avatar/abc?s=192 2x" />';

$avatar = str_replace(
    array('0.gravatar.com', '2.gravatar.com'),
    'cravatar.cn',
    $avatar
);

处理方式是:按数组顺序,一个词一个词地扫描全文,每个词都替换它出现的所有位置。所以上面这段 HTML 里的两个地址,一个 0、一个 2,最后都会变成 cravatar.cn。

回到头像代码:为什么”全部替换”不是凑巧,而是必需

这一点很关键。WordPress 输出的头像 HTML,经常带着两个图片地址:

  • src:普通屏幕用的头像;
  • srcset:高清屏(手机、Mac 这类 Retina 屏)用的 2 倍图。

如果 str_replace 只替换第一个,结果就是:普通屏幕上头像正常,高清屏上依然裂图——修不干净,还留个隐蔽的坑。正是因为它替换全部出现的位置,两个地址才一起被换掉。

一个真实的坑:数组是有顺序的

官方文档专门用了一整块篇幅警告这件事(Replacement order gotcha):数组元素是从前到后依次处理的,前面替换出来的结果,会参与后面的匹配。

具体到头像这个场景,假设哪天您想”省事”,往数组里加一个不带前缀的 gravatar.com,而且把它放在前面:

// 危险写法:笼统的排在前面
$bad = str_replace(
    array('gravatar.com', 'www.gravatar.com'),
    'cravatar.cn',
    'https://www.gravatar.com/avatar/abc'
);
// 结果:https://www.cravatar.cn/avatar/abc   ← www. 前缀残留,照样打不开

原因就是:www.gravatar.com 里本来就含 gravatar.com,第一轮就被换掉了,剩下个光秃秃的 www. 挂在前面。

正确的写法是把更具体、更长的放前面,笼统的放后面:

// 正确写法:具体的排在前面
$good = str_replace(
    array('www.gravatar.com', 'gravatar.com'),
    'cravatar.cn',
    'https://www.gravatar.com/avatar/abc'
);
// 结果:https://cravatar.cn/avatar/abc   ← 干净

顺便说一句,上一篇文章里那段头像代码是安全的:六个域名都是带前缀的完整域名,谁也不是谁的一部分,替换目标 cravatar.cn 里也不含任何搜索词,所以顺序怎么排都不会出问题。唯一的禁忌就是别往里加裸的 gravatar.com。

想彻底避开顺序问题:用 strtr

如果实在不想操心顺序,PHP 还提供了另一个函数 strtr()。它按”最长优先、全文只扫一遍“的方式工作,替换出来的内容不会被二次替换,天然没有上面那个坑:

$avatar = strtr($avatar, array(
    'www.gravatar.com'    => 'cravatar.cn',
    '0.gravatar.com'      => 'cravatar.cn',
    'secure.gravatar.com' => 'cravatar.cn',
));

用哪个都行。str_replace 更常见、可读性更好;strtr 在搜索词互相包含时更稳。我的建议是:先搞清 str_replace 的顺序规则,需要时再换 strtr——知其然也知其所以然,比死记一个”安全写法”有用。

两个自查小技巧

  • 不确定有没有替换成功:用第四个参数 $count,打印出来看看替换了几处。结果是 0,说明压根没匹配上(多半是大小写或者拼写的问题);
  • 注意大小写:str_replace 是区分大小写的。想忽略大小写,用它的兄弟函数 str_ireplace()。

写在最后

这次的收获其实不在代码,而在方法:对自己写的每一行代码,都值得多问一句”如果输入不是我想的那样呢”。字符串里有两个地址怎么办、出现三次怎么办、搜索词互相包含怎么办——把这些问号一个个拉直,代码才真正站得住。

相关文章

Powered by 草根站长笔记 © 2015-2020 草根站长笔记 Inc.版权所有,禁止转载