如何在不重叠结果的情况下替换字符串中的多个字符串?

Posted

技术标签:

【中文标题】如何在不重叠结果的情况下替换字符串中的多个字符串?【英文标题】:How can I replace multiple strings within a string without overlapping results? 【发布时间】:2014-12-24 10:00:18 【问题描述】:

我正在尝试从这样的字符串创建通用掩码:

012abc.d+e_fg~hijk => 012start.d+middle_fg~endjk

替换:

$arrFromTo = array(
              'st' => 'pre',
              'abc' => 'start',
              'e' => 'middle',
              'hi' => 'end',
              'dd' => 'post'
             );

相反,我保持重叠替换并得到类似的东西(使用str_replace的循环):

012preart.d+mipostle_fg~endjk

因为st 位于已替换的start 中,而dd 位于middle 中。

您将如何替换以下内容?

$str = 'abc.d+e_fg~hijk';

echo replace_vars($str); // Desired output: 012start.d+middle_fg~endkJ

【问题讨论】:

我在考虑重新排列数组,以便 'dd' => 'post' 处于不同的位置,但这引发了其他问题。 我不经常建议正则表达式,但在这种情况下,它可能是谨慎的,因此您在替换之前执行前瞻/后向检查以检查您的期望 @scrowler 不幸的是,我的 PCRE 库中有一个错误,带有可变长度的负面外观。我无法升级 PCRE 库,所以我现在停止了这条路。 使用正则表达式时遇到的问题是 php 不支持可变长度的负向回溯。如果是这样,您可以使用如下正则表达式:/(?<!\w*)(e)(?!\w*)/i 这意味着给我任何不在大括号和任何其他单词字符之间的“e”。 \w* 使其成为可变长度的前瞻/后视。显然 pcre 可以很好地处理可变长度的前瞻,但不能很好地处理后瞻。 你可以使用状态机。当您遍历字符串时,保留与替换列表中某些内容的第一个 n 字符匹配的最后一个 n 字符的列表。完全匹配后,将最后一个 n 字符替换为您的替换字符串。 【参考方案1】:

这将按顺序搜索每个替换的字符串。如果找到一个,它将拆分字符串,并搜索字符串的其余部分以查找任何其他替换。

$str = '012abc.d+e_fg~hijk';

$rep = array(
    'st' => 'pre',
    'abc' => 'start',
    'e' => 'middle',
    'hi' => 'end',
    'dd' => 'post'
);

$searched = '';

foreach ($rep as $key => $r) 
    if (strpos($str, $key) !== false) 

        $searched .= substr($str, 0, strpos($str, $key)) . $r;
        $str = substr($str, strpos($str, $key) + strlen($key));

    


$searched .= $str;

echo $searched; //012start.d+middle_fg~endjk

它将按照您指定的顺序搜索并找到它们。

【讨论】:

【参考方案2】:

我可能会误解,但您似乎不需要正则表达式来替换。它们是简单的字面替换。

$from = '012abc.d+e_fg~hijk';
$arrFromTo = array(
              'st' => 'pre',
              'abc' => 'start',
              'e' => 'middle',
              'hi' => 'end',
              'dd' => 'post'
             );
$to = strtr($from, $arrFromTo); // 012start.d+middle_fg~endjk

strtr() 太棒了。它需要一个非常易读的输入,并且不会像循环中的问题那样重新替换。

【讨论】:

【参考方案3】:

你可以像这样使用preg_replace

$str = '012abc.d+e_fg~hijk';
$arrFromTo = array(
              'st' => 'pre',
              'abc' => 'start',
              'e' => 'middle',
              'hi' => 'end',
              'dd' => 'post'
             );

$reArr=array();
foreach($arrFromTo as $k=>$v)
   $reArr['/' . $k . '(?![^]*)/'] = $v;


echo preg_replace(array_keys($reArr), array_values($reArr), $str);
//=> 012start.d+middle_fg~endjk

这个正则表达式的核心是这个否定的lookaead:(?![^]*)

如果数组的键包含在... 中,则避免匹配键,因为所有替换都包含在... 中。

【讨论】:

为什么是preg_replace?为什么不str_replacestrtr?不需要正则表达式,是吗? 如何在str_replace 中使用前瞻? 为什么需要前瞻?它们是文字替换......源字符串中没有 哦,好吧,所以str_replace 不如strtr 聪明。太糟糕了。现在我们知道了! 是的没错,但你对strtr+1 的回答还是很正确的。

以上是关于如何在不重叠结果的情况下替换字符串中的多个字符串?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不重叠的情况下显示多个小吃店

如何在不使用大型结果集的情况下检查一个字符串是不是是另一个字符串的子字符串?

如何在不将空值发送到输出数组的情况下拆分字符串

如何在不同步的情况下使用多个线程(2、4、8、16 个线程)循环打印字符串(10,100、1000 个循环)?

如何在不使用集合的情况下返回多个值?

如何在不重叠网页内容的情况下缩小背景图片的大小