替换字符串中最后一次出现的字符串

Posted

技术标签:

【中文标题】替换字符串中最后一次出现的字符串【英文标题】:Replace last occurrence of a string in a string 【发布时间】:2011-04-19 15:15:51 【问题描述】:

有人知道用字符串中的另一个字符串替换最后一次出现的字符串的快速方法吗?

注意,最后出现的字符串可能不是字符串中的最后一个字符。

例子:

$search = 'The';
$replace = 'A';
$subject = 'The Quick Brown Fox Jumps Over The Lazy Dog';

预期输出:

The Quick Brown Fox Jumps Over A Lazy Dog

【问题讨论】:

您可能会发现 s($str)->replaceLast($search, $replace) 很有帮助,如在 this standalone library 中找到的那样。 【参考方案1】:

你可以使用这个功能:

function str_lreplace($search, $replace, $subject)

    $pos = strrpos($subject, $search);

    if($pos !== false)
    
        $subject = substr_replace($subject, $replace, $pos, strlen($search));
    

    return $subject;

【讨论】:

无论如何,这仍然返回 true。考虑将其修改为: if($pos) $subject = substr_replace($subject, $replace, $pos, strlen($search));返回$主题; 否则 返回假; @Jason 无论如何它都不会返回TRUE。无论如何它都会返回一个字符串。如果无法进行替换,则返回原始的$subject,就像substr_replace str_replace 一样。 @Mischa 在这种情况下不是一样的吗?我试图做类似 !str_lreplace 的事情,但如果它不返回 false,它被认为是 true,对吗?无论哪种方式,这都帮助了我,我很感激。谢谢。 这如何工作? strpos — Find the position of the first occurrence of a substring in a string - 编辑:哇。 php 天才真的做了一个函数叫strposstrrpos 吗?谢谢.... @Barry 这是 PHP 不应该受到指责的一种情况 :-) 名称以标准 C 库的几十年前的 strstrstrrstr 为模式,它们是相同的职能。 (但他们必须改名吗?)【参考方案2】:

这也可以:

function str_lreplace($search, $replace, $subject)

    return preg_replace('~(.*)' . preg_quote($search, '~') . '(.*?)~', '$1' . $replace . '$2', $subject, 1);

更新稍微简洁一点的版本 (http://ideone.com/B8i4o):

function str_lreplace($search, $replace, $subject)

    return preg_replace('~(.*)' . preg_quote($search, '~') . '~', '$1' . $replace, $subject, 1);

【讨论】:

我做错了吗?如果是这样,请忽略我:) ||| echo str_lreplace("x", "y", "this x or that x"); => 输出:“y”参见:ideone.com/UXuTo @edorian:哎呀!抱歉,我发的比较匆忙,正确的版本在这里:ideone.com/vR073。 这个答案缺少教育解释。【参考方案3】:
$string = 'this is my world, not my world';
$find = 'world';
$replace = 'farm';
$result = preg_replace(strrev("/$find/"),strrev($replace),strrev($string),1);
echo strrev($result); //output: this is my world, not my farm

【讨论】:

为什么它适用于所有颠倒的字符串?使用正则表达式时是否有一些(我假设)特定的性能提升? 不,它实际上会降低性能,但这是因为您只希望最后一次出现,因此您将搜索限制为一个并反转所有内容,如果您希望第一次出现,则无需反转任何内容【参考方案4】:

另一个 1-liner 但没有 preg:

$subject = 'bourbon, scotch, beer';
$search = ',';
$replace = ', and';

echo strrev(implode(strrev($replace), explode(strrev($search), strrev($subject), 2))); //output: bourbon, scotch, and beer

【讨论】:

FWIW,公认的解决方案比这个解决方案快大约 35%。 我总是选择一个正则表达式函数而不是 6 个函数调用。我无法想象在专业代码中看到这种技术。【参考方案5】:

在 reg 表达式上使用“$”来匹配字符串的结尾

$string = 'The quick brown fox jumps over the lazy fox';
echo preg_replace('/fox$/', 'dog', $string);

//output
'The quick brown fox jumps over the lazy dog'

【讨论】:

仅当最后一次出现在字符串 ideone.com/nbNSNq 的末尾时才有效 如果在最后一个 'fox' 之后出现任何其他字符,这将不起作用。【参考方案6】:

以下相当紧凑的解决方案使用PCRE positive lookahead assertion 来匹配感兴趣的子字符串的最后一次出现,即该子字符串的出现之后没有任何其他相同子字符串的出现。因此,该示例将last 'fox' 替换为'dog'

$string = 'The quick brown fox, fox, fox jumps over the lazy fox!!!';
echo preg_replace('/(fox(?!.*fox))/', 'dog', $string);

输出:

The quick brown fox, fox, fox jumps over the lazy dog!!!

【讨论】:

我不明白为什么这个后来的答案比Alix Axel's answer 获得了更多的支持。与 Alix 的模式相比,这种模式需要 3 倍的“步骤”。负前瞻是不必要的开销,因为它根本不会提高准确性 - 与贪婪的点匹配相比。【参考方案7】:

你可以这样做:

$str = 'Hello world';
$str = rtrim($str, 'world') . 'John';

结果是“你好约翰”;

【讨论】:

只要没有任何重复的字符就可以使用。在我的情况下,我将页码从存档日期中删除,所以我有“2015-12/2”,它把所有/和所有 2 从结尾变成“2015-1”。 这只有在搜索的最后一个匹配项是最后一个单词并且后面没有其他字符时才有效。 这不起作用,因为rtrim 的行为与您的想法不符。它将以任何顺序从末尾删除搜索字符串中存在的任何字符(并始终附加替换),例如“Hello word”->“Hello John”、“Hello lord”->“Hello John”、“Hello motor”->“Hello motJohn”、“Hello worldy”->“Hello worldyJohn”。 我不建议任何人使用这个容易悄悄失败的(无法解释的)代码。这个答案没有尝试“找到”字符串的最后一次出现——它只是在满足字符掩码的同时不断消耗输入字符串右侧的字符。这做同样的事情:rtrim($str, 'dlorw')【参考方案8】:

您可以使用 strrpos() 查找最后一个匹配项。

$string = "picture_0007_value";
$findChar =strrpos($string,"_");

$string[$findChar]=".";

echo $string;

输出:picture_0007.value

【讨论】:

这仅在替换长度为 1 且搜索字符存在的单字节字符串时是正确的——在所有其他情况下,这将失败。【参考方案9】:
$string = "picture_0007_value";
$findChar =strrpos($string,"_");
if($findChar !== FALSE) 
  $string[$findChar]=".";


echo $string;

除了代码中的错误之外,Faruk Unal 拥有最好的答案。一个函数就可以解决问题。

【讨论】:

您需要检查 $findChar 是否为假(与接受的答案相同)。如果字符串不包含搜索到的字符串,您会收到通知并替换第一个字符。 这很好,但目前它只能用 1 个字符替换 1 个字符。 上述cmets是正确的。这仅在替换长度为 1 的单字节字符串时是正确的。【参考方案10】:

已接受答案的简写

function str_lreplace($search, $replace, $subject) 
    return is_numeric($pos=strrpos($subject,$search))?
    substr_replace($subject,$replace,$pos,strlen($search)):$subject;

【讨论】:

这也比接受的答案可读性差。为什么不使用更好的编码标准?请阅读 PSR 编码标准。【参考方案11】:

感兴趣的人:我编写了一个利用 preg_match 的函数,以便您可以使用正则表达式从右侧替换。

function preg_rreplace($search, $replace, $subject) 
    preg_match_all($search, $subject, $matches, PREG_SET_ORDER);
    $lastMatch = end($matches);

    if ($lastMatch && false !== $pos = strrpos($subject, $lastMatchedStr = $lastMatch[0])) 
        $subject = substr_replace($subject, $replace, $pos, strlen($lastMatchedStr));
    

    return $subject;

或者作为两个选项的简写组合/实现:

function str_rreplace($search, $replace, $subject) 
    return (false !== $pos = strrpos($subject, $search)) ?
        substr_replace($subject, $replace, $pos, strlen($search)) : $subject;

function preg_rreplace($search, $replace, $subject) 
    preg_match_all($search, $subject, $matches, PREG_SET_ORDER);
    return ($lastMatch = end($matches)) ? str_rreplace($lastMatch[0], $replace, $subject) : $subject;

基于https://***.com/a/3835653/3017716 和https://***.com/a/23343396/3017716

【讨论】:

preg_ 技术会产生警告,不应使用。 3v4l.org/lNU8R【参考方案12】:

这是一个古老的问题,但为什么每个人都忽略了最简单的基于正则表达式的解决方案?正常的正则表达式量词是贪婪的,人!如果要查找模式的最后一个实例,只需将.* 放在它前面。方法如下:

$text = "The quick brown fox, fox, fox, fox, jumps over etc.";
$fixed = preg_replace("((.*)fox)", "$1DUCK", $text);
print($fixed);

这会将“fox”的最后一个实例替换为“DUCK”,就像它应该做的那样,并打印:

The quick brown fox, fox, fox, DUCK, jumps over etc.

【讨论】:

谢谢!包裹我的表情以完成此任务的完美功能。在我的情况下,我将最后一个逗号替换为“,”和“。很高兴我在这个答案列表中向下滚动了一点。 “最简单的基于正则表达式的解决方案”被您“忽略”了——这项技术是posted 8 years earlier on this page by Alix Axel。他/她的答案是相同的,只是他/她的 sn-p 是动态的。您的答案已将针头硬编码到搜索模式中,并将替换字符串硬编码到替换参数中。我通过使用\K 字符here 进一步简化了替换 @mickmackusa,你说得对,很好。确实,Alix Axel 的答案在左侧使用了贪婪搜索......但我们只是说我不认为它特别“简单”:-D。 (也没有理由使用 ~ 作为分隔符......只会让事情变得更加混乱。) 我认为您的括号分隔符对我来说更令人困惑——而且我对阅读正则表达式非常满意。我经常使用波浪线作为分隔符,即使模式中没有正斜杠——这是我个人的偏好。看到模式中的括号,我的脑海中会假设一个捕获组或环视。 Alix 的答案不那么简单,主要是因为它是动态的。我的回答稍微收紧了语法。 括号一个捕获组。【参考方案13】:

一个简短的版本:

$NewString = substr_replace($String,$Replacement,strrpos($String,$Replace),strlen($Replace));

【讨论】:

不应使用这个无法解释的“短版本”(可读性较差的版本),因为如果找不到针 ($Replace),则字符串 ($String) 会意外损坏/突变。这是早期答案没有犯的错误。应删除此答案,以免研究人员复制此不适当的 sn-p。 3v4l.org/easUY【参考方案14】:

虽然使用正则表达式的性能通常不如非正则表达式技术,但我非常欣赏它所提供的控制力和灵活性。

在我的 sn-p 中,我将模式设置为不区分大小写(\i,尽管我的示例输入不会挑战此规则)并包括单词边界(\b,尽管没有明确要求它们)。

我还将使用\K 元字符来重置完整字符串匹配,这样就不需要捕获组/反向引用。

代码:(Demo)

$search = 'The';
$replace = 'A';
$subject = "The Quick Brown Fox Jumps Over The Lazy Dog's Thermos!";

echo preg_replace(
         '/.*\K\b' . preg_quote($search, '/') . '\b/i',
         $replace,
         $subject
     );

输出:

  The Quick Brown Fox Jumps Over A Lazy Dog's Thermos!
# ^^^                            ^            ^^^
# not replaced                replaced        not replaced

不限字数:(Demo)

echo preg_replace(
         '/.*\K' . preg_quote($search, '/') . '/i',
         $replace,
         $subject
     );

输出:

  The Quick Brown Fox Jumps Over The Lazy Dog's Armos!
# ^^^                            ^^^            ^
# not replaced              not replaced        replaced

【讨论】:

不要将字符串与单词混淆。单词是空格分隔的字符串,字符串可以是单词的一部分。正确的代码应该将“Thermos”转换为“Aermos”,并且保持“The Lazy”不变。 是的。尽管没有要求,但我故意使用单词边界。 OP 没有minimal reproducible example,所以我们不知道是否需要区分大小写和字边界。我很慷慨,并为研究人员可能想要的页面提供了几种新技术。如果您愿意,我可以扩展我的答案并添加另一个无边界的 sn-p。【参考方案15】:

下面更正了 zack 的代码 (https://***.com/a/11144999/9996503)。 小心正则表达式!考虑一下:

$string = 'Round brackets (parentheses) "()", square brackets "()"';
$find = '()';
$replace = '[]';
// Zack's code:
$result = preg_replace(strrev("/$find/"),strrev($replace),strrev($string),1);
var_dump($result); // Output: NULL
// Correct code:
$result = strrev(preg_replace('/'.preg_quote(strrev($find)).'/', strrev($replace), strrev($string), 1));
echo $result; //Output: Round brackets (parentheses) "()", square brackets "[]"

【讨论】:

以上是关于替换字符串中最后一次出现的字符串的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript:替换字符串中最后一次出现的文本

使用 sed 替换字符串中最后一次出现的字符

使用 sed 替换字符串中最后一次出现的字符

用另一个字符串替换第一次和最后一次出现的字符串

替换字符串的最后一部分

替换 Python 中第一次出现的字符串