str_replace函数异常行为问题及替代实现方案咨询
我有一段用户可设置的文本,默认内容为[Log in] or [register] to view the content。需要将方括号中的两个内容分别包裹到对应链接中,但首先要验证用户未修改默认文本(只需检查文本中存在两组方括号即可),此时默认第一个对应登录页链接,第二个对应注册页链接。
我用以下代码判断文本是否符合条件:
if ( preg_match( '/\[(.*?)\].*\[(.*?)\]/', $text ) )
判断成立后,尝试通过数组形式调用str_replace实现替换:
$text = str_replace( array( '[', ']', '[', ']' ), array( '%1$s', '%2$s', '%3$s', '%4$s' ), $text );
预期得到%1$sLog in%2$s or %3$sregister%4$s to view the content,但实际结果是%1$sLog in%2$s or %1$sregister%2$s to view the content,想知道原因和正确实现方式。
str_replace处理数组替换的逻辑是:按顺序遍历查找数组,遇到匹配的字符串就用对应位置的替换值,但如果查找数组中有重复项,所有匹配该重复项的位置都会使用第一个对应的替换值。
具体到你的代码,查找数组里的前两个元素是[和],对应替换值%1$s和%2$s;后面的[和]是重复项,str_replace不会“记住”已经替换过一次,遇到第二个[时,还是会匹配查找数组中第一个[对应的%1$s,第二个]匹配第一个]对应的%2$s,所以出现了不符合预期的结果。
方法一:使用preg_replace直接替换(推荐)
既然已经通过preg_match验证了文本格式,直接用正则捕获分组一步完成替换,既简洁又准确:
// 假设登录页链接为/login,注册页链接为/register $text = preg_replace('/\[(.*?)\].*\[(.*?)\]/', '<a href="/login">$1</a> or <a href="/register">$2</a> to view the content', $text);
如果需要先替换为占位符再后续处理,可以指定替换次数分别处理两组方括号:
// 只替换第一组方括号 $text = preg_replace('/\[(.*?)\]/', '%1$s$1%2$s', $text, 1); // 只替换第二组方括号 $text = preg_replace('/\[(.*?)\]/', '%3$s$1%4$s', $text, 1);
这里第三个参数1表示仅替换一次,确保两组方括号分别对应不同的占位符。
方法二:拆分字符串拼接
通过explode拆分文本,提取方括号内的内容后重新拼接:
$parts = explode('[', $text); $loginText = explode(']', $parts[1])[0]; $registerText = explode(']', $parts[2])[0]; $text = "%1$s{$loginText}%2$s or %3$s{$registerText}%4$s to view the content";
内容的提问来源于stack exchange,提问作者Faye D.

