使用str_replace,使其只作用于第一个匹配的对象?

回答 23 浏览 30.7万 2009-08-10

我想要一个str_replace()的版本,只替换$subject$search的第一次出现。是否有一个简单的解决方案,或者我需要一个黑客解决方案?

Nick Heiner 提问于2009-08-10
你可能会发现s($subject)->replaceFirst($search)s($subject)->replaceFirstIgnoreCase($search)@有帮助,在这个独立的库中可以看到。caw 2016-07-26
你可能想知道$count参数是做什么用的,它是用于OUTPUT--有多少次替换被执行了--不是一个限制,所以这是一个完全有效的问题。这在其他语言中可能会引起混淆,因为例如Python的str.replace确实使用count param作为限制参数。jave.web 2020-12-04
很好的问题,得到了大家的支持。只是一个想法,也许可以重新考虑一个已接受的答案?干杯!HoldOffHunger 2021-11-07
23 个回答
#1楼
得票数 737

没有任何版本,但解决方案一点也不黑。

$pos = strpos($haystack, $needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
}

相当简单,而且省去了正则表达式的性能损失。


奖励:如果您想替换 last 出现,只需使用 strrpos 代替 strpos

zombat 提问于2009-08-10
Eaten by a Grue 修改于2016-12-04
可以比正则表达式快得多,使用的内存也会更少。不知道为什么有人会投票反对......Josh Davis 2009-08-10
我喜欢这种方法,但是代码中有一个错误,substr_replace调用的最后一个参数应该是strlen($needle)而不是strlen($replace),请注意这个问题Nelson 2010-09-21
它是 "黑客",因为它需要相当多的时间来弄清楚发生了什么事。另外,如果它是清晰的代码,就不会提到代码有错误。如果在这么小的片段中都有可能出错,那就已经太黑了。Camilo Martin 2013-11-12
我不同意@CamiloMartin关于行数与错误的可能性的看法。虽然由于所有的参数,substr_replace是一个有点不方便使用的函数,但真正的问题是,用数字做字符串操作有时就是棘手--你必须小心地把正确的变量/偏移量传给函数。实际上,我甚至可以说上面的代码是最直接的,对我来说也是最符合逻辑的方法。Alex 2014-04-22
出色的方法。在替换含有保留的regex字符的变量值时,效果完美(所以preg_replace是熊的)。这很直接,也很优雅。Praesagus 2014-11-07
#2楼 已采纳
得票数 394

可以用preg_replace来完成。

function str_replace_first($search, $replace, $subject)
{
    $search = '/'.preg_quote($search, '/').'/';
    return preg_replace($search, $replace, $subject, 1);
}

echo str_replace_first('abc', '123', 'abcdef abcdef abcdef'); 
// outputs '123def abcdef abcdef'

神奇之处在于可选的第四个参数[限制]。来自文档。

[Limit] - 每个主题字符串中每个模式的最大可能替换。默认为-1(无限制)。


不过,请参阅zombat的回答,以了解更有效的方法(大致上,3-4倍的速度)。

karim79 提问于2009-08-10
Jonathan 修改于2021-11-18
这种方法的缺点是,正则表达式的性能受到影响。zombat 2009-08-10
另一个缺点是你必须在 "针 "上使用preg_quote(),并在替换中转义元字符$和/或。Josh Davis 2009-08-10
由于讨厌的转义问题,作为一个通用的解决方案,这是不成功的。Jeremy Kauffman 2011-07-09
正则表达式常常因为 "性能 "而被否定,如果性能是首要考虑的问题,我们就不会写PHP了除了'/'之外,还可以用其他的东西来包裹这个模式,也许是'~',这样可以在一定程度上避免转义的问题。这取决于数据是什么,以及它来自哪里。ThomasRedstone 2015-12-11
除了性能上的缺点外--那些抱怨转义问题的人,除了preg_quote的潜在错误外,还有什么具体的想法吗?例如,@ThomasRedstone担心/的分隔符如果出现在$from中会很危险,但幸运的是它没有:由于preg_quote的第二个参数,它被正确地转义了(我们可以很容易地测试)。我很想知道具体的问题(在我看来,这将是严重的PCRE安全漏洞)。MvanGeest 2017-01-31
#3楼
得票数 102

编辑:两个答案都已更新,现在是正确的。我将留下这个答案,因为函数的时序仍然有用。

"僵尸 "和 "太多的php "的答案很不幸地不正确。这是对僵尸发布的答案的修改(因为我没有足够的声誉来发布评论)。

$pos = strpos($haystack,$needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack,$replace,$pos,strlen($needle));
}

注意strlen($needle),而不是strlen($replace)。Zombat的例子只有在needle和replace长度相同的情况下才能正确工作。

在一个与PHP自己的str_replace签名相同的函数中,也有同样的功能。

function str_replace_first($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos !== false) {
        return substr_replace($subject, $replace, $pos, strlen($search));
    }
    return $subject;
}

这是修订后的 "太多的php "的答案。

implode($replace, explode($search, $subject, 2));

注意末尾的2而不是1,或者说是函数格式。

function str_replace_first($search, $replace, $subject) {
    return implode($replace, explode($search, $subject, 2));
}

我对这两个函数进行了计时,当没有找到匹配的时候,第一个函数的速度是两倍。当找到匹配的时候,它们的速度是一样的。

Bas 提问于2010-04-09
Gras Double 修改于2016-02-26
为什么不把它泛化为:str_replace_flexible(mixed $s, mixed $r, int $offset, int $limit),该函数从$offset(第n个)匹配处开始替换$limit的出现。Adam Friedman 2014-05-23
太糟糕了,这只适用于区分大小写的替换方式。andreszs 2015-03-19
@Andrew stripos() 拯救了我们 :-)Gras Double 2016-02-13
#4楼
得票数 93

我想知道哪一个是最快的,所以我把它们都测试了一遍。

下面你将会发现。

  • 所有被贡献到本页面的功能的全面列表
  • 每项对比的基准测试(10,000次运行的平均执行时间)。
  • 每个答案的链接(用于完整的代码)。

所有功能都在相同的设置下进行了测试。

$string = 'OOO.OOO.OOO.S';
$search = 'OOO'; 
$replace = 'B';

只替换一个字符串中第一个出现的字符串的函数。


只替换字符串中最后一次出现的字符串的函数。

oLinkWebDevelopment 提问于2014-03-08
Community 修改于2017-05-23
谢谢你,我通常使用preg_replace,因为它是最灵活的,如果将来需要调整,在大多数情况下,27%的速度不会很明显。zzapper 2016-01-28
@oLinkWebDevelopment 我很想看看你的基准脚本。我认为它可能被证明是有用的。Dave Morton 2017-04-07
substr_replace()赢得结果的原因很简单;因为它是一个内部函数。两个做同样事情的内部函数和用户定义的函数在性能上是不同的,因为内部函数在低层运行。那么,为什么不是preg_match()?正则表达式几乎比每一个内部的字符串操作函数都要慢,因为它们是在一个字符串中多次搜索的国家。MAChitgarha 2018-09-21
我希望你的 "赢家"(substr_replace($string, $replace, 0, strlen($search));)上的基准不只是写了那个静态的0。非重合的解决方案的一部分是,它们需要 "找到 "起点,然后才知道在哪里进行替换。mickmackusa 2019-02-20
#5楼
得票数 56

不幸的是,我不知道有什么PHP函数可以做到这一点。
你可以像这样相当容易地推出你自己的函数。

function replace_first($find, $replace, $subject) {
    // stolen from the comments at PHP.net/str_replace
    // Splits $subject into an array of 2 items by $find,
    // and then joins the array with $replace
    return implode($replace, explode($find, $subject, 2));
}
too much php 提问于2009-08-10
Alex Tartan 修改于2016-08-18
我认为这是最愚蠢的版本--使用join而不是implodeTitus 2017-11-21
return implode($replace, explode($find, $subject, $limit+1));,用于自定义替换数字beppe9000 2019-12-07
#6楼
得票数 7

我创建了这个函数,用limit来替换字符串(区分大小写),不需要Regexp。它运行良好。

function str_replace_limit($search, $replace, $string, $limit = 1) {
    $pos = strpos($string, $search);

    if ($pos === false) {
        return $string;
    }

    $searchLen = strlen($search);

    for ($i = 0; $i < $limit; $i++) {
        $string = substr_replace($string, $replace, $pos, $searchLen);

        $pos = strpos($string, $search);

        if ($pos === false) {
            break;
        }
    }

    return $string;
}

举例说明用法。

$search  = 'foo';
$replace = 'bar';
$string  = 'foo wizard makes foo brew for evil foo and jack';
$limit   = 2;

$replaced = str_replace_limit($search, $replace, $string, $limit);

echo $replaced;
// bar wizard makes bar brew for evil foo and jack
Parziphal 提问于2011-12-09
Parziphal 修改于2016-03-23
虽然我宁愿做===false,而不是is_bool(,以更明确的方式 - 我给这个大拇指,只是因为它避免了疯狂的正则表达式 !...同时,它也是有效的,并且是干净的解决方案...jave.web 2016-03-14
更喜欢一个容易定制的preg_解决方案并不是疯狂,而是一种个人偏好。return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);对于不惧怕重码的人来说是相当简单的。需要不区分大小写的搜索?在末尾的模式分隔符后添加i。需要单字节/多字节支持?在末尾模式分隔符后添加u。需要词的边界支持?在你的搜索字符串的两边添加\b。如果你不希望使用重合码,就不要使用重合码。马的课程,但肯定不是疯狂的。mickmackusa 2019-02-20
#7楼
得票数 5
$str = "/property/details&id=202&test=123#tab-6p";
$position = strpos($str,"&");
echo substr_replace($str,"?",$position,1);

使用substr_replace,我们可以只替换字符串中出现的第一个字符。由于&重复了多次,但只在第一个位置出现,我们必须将&替换为?

pooja goyal 提问于2019-10-29
#8楼
得票数 5

=> 代码已被修改,所以考虑到一些评论太旧了

并且感谢大家帮助我改进这一点

任何BUG,请与我联系;我将立即解决这个问题。

因此,让我们为之努力。

将第一个 'o' 替换为 'ea' 例如:

$s='I love you';
$s=str_replace_first('o','ea',$s);
echo $s;

//output: I leave you

该功能。

function str_replace_first($this,$that,$s)
{
    $w=strpos($s,$this);
    if($w===false)return $s;
    return substr($s,0,$w).$that.substr($s,$w+strlen($this));
}
PYK 提问于2016-01-10
Sabrina 修改于2022-07-20
如果$this有重复的字符,如aaa vs aaaaaaaaa,则失败。Cristo 2016-06-13
我认为这应该是substr($where,$b+strlen($this)),而不是substr($where,$b+1)。而且我想,substr_replace更快。Titus 2017-11-21
代码已被修改,现在即使是长字符串也能正常工作。PYK 2019-02-12
这个解决方案并不像编码中的那样有效。证明。3v4l.org/cMeZj 而当你解决了变量命名的问题后,当搜索值没有找到时,它就不起作用了--它破坏了输入字符串。证明。3v4l.org/XHtfcmickmackusa 2019-02-20
有人要求FIX代码,这公平吗?@mickmackusa 你能再检查一下吗?PYK 2019-02-20
#9楼
得票数 3

最简单的方法是使用正则表达式。

另一种方法是用strpos()找到字符串的位置,然后用substr_replace()进行替换。

但是,我真的会选择正则表达法。

Rufinus 提问于2009-08-10
与本页的其他帖子相比,这个 "提示 "相当含糊/低价值。mickmackusa 2019-02-20
#10楼
得票数 3
function str_replace_once($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos === false) {
        return $subject;
    }

    return substr($subject, 0, $pos) . $replace . substr($subject, $pos + strlen($search));
}
luchaninov 提问于2014-09-06
仅有代码的答案在StackOverflow上是低价值的,因为它们在教育/增强成千上万的未来研究人员的能力方面做得很差。mickmackusa 2019-02-20
#11楼
得票数 2
$string = 'this is my world, not my world';
$find = 'world';
$replace = 'farm';
$result = preg_replace("/$find/",$replace,$string,1);
echo $result;
zack 提问于2012-06-21
这和第一个答案是一样的。此外,你应该在使用它作为表达式之前对$find做一个preg_quoteEmil Vikström 2012-06-21
这是我用过的,所以我把它投了上去。第一个答案引起了与Drupal的冲突,它一定是覆盖了一个Drupal的辅助函数。所以我只是把函数里面的代码拿出来,和其他的代码一起使用......Dan Mantyla 2017-09-19
这个只有代码的答案在页面上提供了多余的建议(更不用说它缺乏preg_quote()。这个迟到的重复答案可以被安全地从页面上清除,因为它的建议是由较早的、被接受的高票数的答案提供的。mickmackusa 2019-02-20
#12楼
得票数 2

为了扩展@renocor 的答案,我编写了一个与str_replace() 100% 向后兼容的函数。也就是说,您可以用str_replace_limit() 替换所有 次出现的str_replace(),而不会搞砸任何事情,即使是那些为$search$replace 和/或$subject 使用数组的人。

如果你想用($string===strval(intval(strval($string))))代替函数调用,该函数可以完全自成一体,但我建议不要这样做,因为在处理以字符串形式提供的整数时,valid_integer()是一个相当有用的函数。

注意:只要有可能,str_replace_limit()将使用str_replace()代替,因此所有对str_replace()的调用都可以用str_replace_limit()代替,而不必担心对性能的打击。

Usage

<?php
$search = 'a';
$replace = 'b';
$subject = 'abcabc';
$limit = -1; // No limit
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

2名替补 -- -- bbcbbc

$limit = 1; // Limit of 1
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

1名替代者 -- -- BBCABC

$limit = 10; // Limit of 10
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

2名替补 -- -- bbcbbc

Function

<?php

/**
 * Checks if $string is a valid integer. Integers provided as strings (e.g. '2' vs 2)
 * are also supported.
 * @param mixed $string
 * @return bool Returns boolean TRUE if string is a valid integer, or FALSE if it is not 
 */
function valid_integer($string){
    // 1. Cast as string (in case integer is provided)
    // 1. Convert the string to an integer and back to a string
    // 2. Check if identical (note: 'identical', NOT just 'equal')
    // Note: TRUE, FALSE, and NULL $string values all return FALSE
    $string = strval($string);
    return ($string===strval(intval($string)));
}

/**
 * Replace $limit occurences of the search string with the replacement string
 * @param mixed $search The value being searched for, otherwise known as the needle. An
 * array may be used to designate multiple needles.
 * @param mixed $replace The replacement value that replaces found search values. An
 * array may be used to designate multiple replacements.
 * @param mixed $subject The string or array being searched and replaced on, otherwise
 * known as the haystack. If subject is an array, then the search and replace is
 * performed with every entry of subject, and the return value is an array as well. 
 * @param string $count If passed, this will be set to the number of replacements
 * performed.
 * @param int $limit The maximum possible replacements for each pattern in each subject
 * string. Defaults to -1 (no limit).
 * @return string This function returns a string with the replaced values.
 */
function str_replace_limit(
        $search,
        $replace,
        $subject,
        &$count,
        $limit = -1
    ){

    // Set some defaults
    $count = 0;

    // Invalid $limit provided. Throw a warning.
    if(!valid_integer($limit)){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting an '.
                'integer', E_USER_WARNING);
        return $subject;
    }

    // Invalid $limit provided. Throw a warning.
    if($limit<-1){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                'a positive integer', E_USER_WARNING);
        return $subject;
    }

    // No replacements necessary. Throw a notice as this was most likely not the intended
    // use. And, if it was (e.g. part of a loop, setting $limit dynamically), it can be
    // worked around by simply checking to see if $limit===0, and if it does, skip the
    // function call (and set $count to 0, if applicable).
    if($limit===0){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                'a positive integer', E_USER_NOTICE);
        return $subject;
    }

    // Use str_replace() whenever possible (for performance reasons)
    if($limit===-1){
        return str_replace($search, $replace, $subject, $count);
    }

    if(is_array($subject)){

        // Loop through $subject values and call this function for each one.
        foreach($subject as $key => $this_subject){

            // Skip values that are arrays (to match str_replace()).
            if(!is_array($this_subject)){

                // Call this function again for
                $this_function = __FUNCTION__;
                $subject[$key] = $this_function(
                        $search,
                        $replace,
                        $this_subject,
                        $this_count,
                        $limit
                );

                // Adjust $count
                $count += $this_count;

                // Adjust $limit, if not -1
                if($limit!=-1){
                    $limit -= $this_count;
                }

                // Reached $limit, return $subject
                if($limit===0){
                    return $subject;
                }

            }

        }

        return $subject;

    } elseif(is_array($search)){
        // Only treat $replace as an array if $search is also an array (to match str_replace())

        // Clear keys of $search (to match str_replace()).
        $search = array_values($search);

        // Clear keys of $replace, if applicable (to match str_replace()).
        if(is_array($replace)){
            $replace = array_values($replace);
        }

        // Loop through $search array.
        foreach($search as $key => $this_search){

            // Don't support multi-dimensional arrays (to match str_replace()).
            $this_search = strval($this_search);

            // If $replace is an array, use the value of $replace[$key] as the replacement. If
            // $replace[$key] doesn't exist, just an empty string (to match str_replace()).
            if(is_array($replace)){
                if(array_key_exists($key, $replace)){
                    $this_replace = strval($replace[$key]);
                } else {
                    $this_replace = '';
                }
            } else {
                $this_replace = strval($replace);
            }

            // Call this function again for
            $this_function = __FUNCTION__;
            $subject = $this_function(
                    $this_search,
                    $this_replace,
                    $subject,
                    $this_count,
                    $limit
            );

            // Adjust $count
            $count += $this_count;

            // Adjust $limit, if not -1
            if($limit!=-1){
                $limit -= $this_count;
            }

            // Reached $limit, return $subject
            if($limit===0){
                return $subject;
            }

        }

        return $subject;

    } else {
        $search = strval($search);
        $replace = strval($replace);

        // Get position of first $search
        $pos = strpos($subject, $search);

        // Return $subject if $search cannot be found
        if($pos===false){
            return $subject;
        }

        // Get length of $search, to make proper replacement later on
        $search_len = strlen($search);

        // Loop until $search can no longer be found, or $limit is reached
        for($i=0;(($i<$limit)||($limit===-1));$i++){

            // Replace 
            $subject = substr_replace($subject, $replace, $pos, $search_len);

            // Increase $count
            $count++;

            // Get location of next $search
            $pos = strpos($subject, $search);

            // Break out of loop if $needle
            if($pos===false){
                break;
            }

        }

        // Return new $subject
        return $subject;

    }

}
0b10011 提问于2012-07-09
Community 修改于2017-05-23
如果你问我的话,我觉得这个方案太臃肿了。另外,我最 "讨厌 "的是这个解决方案的错误处理。如果你传递了不正确的值,它就会破坏脚本。你认为它看起来很专业,但其实不然,它不是一个错误,而是产生一个通知或警告。更好的办法是跳过这些废话,返回false或者null,并且永远不要在这样的函数中使用反向追踪。最好的解决办法是,程序员可以决定当输出错误/意外时该怎么做。Codebeat 2013-07-16
@Erwinus 这句话自始至终都在使用E_USER_WARNING,这是一个警告不是一个错误。回溯是非常有用的,可以找出哪些代码首先将无效的数据传递给了函数(这对于追踪生产中的bug是绝对必要的)。至于返回$subject而不是false/null或抛出一个错误,这只是我在使用情况下的个人选择。为了与str_replace()的功能相匹配,使用可捕捉的致命错误将是最好的选择(就像str_replace()为前两个参数提供一个闭合时那样)。0b10011 2013-07-17
啊,没有注意到你所使用的E_USER_WARNING,对此很抱歉。返回主题的问题是,在函数之外,你永远无法看到有什么问题。也就是说,如果你做得更聪明的话,这个函数的大小可以减少一半(这是可能的)。第二,注释在解释复杂的东西时很好,但对于增加一个值这样简单的事情却不是很有用。总的来说,我认为它是不必要的巨大。另外,在生产环境中使用警告可能是一个安全问题,当你在一个默认不抑制运行时消息的服务器上使用这段代码时(日志)。Codebeat 2013-07-17
@Erwinus 我在评论的时候很啰嗦,因为有些人对语言的理解不如其他人,而评论总是可以被那些理解它的人删除。如果你知道有更好的方法可以对所有的边缘情况获得相同的最终结果,通过各种方式,编辑答案。如果你的生产环境没有抑制错误信息,那么你的问题就比这个函数大了;)0b10011 2013-07-17
TL;DR 这个片段非常臃肿,我无法想象选择它而不是一个regex函数(我讨厌滚动)。如果你想计算所做的替换,在preg_replace()中有一个参数。此外,preg_replace()/regex提供了单词边界处理(如果需要的话)--这是非regex函数无法优雅地提供的。mickmackusa 2019-02-20
#13楼
得票数 2

根据我的测试结果,我想投票给karim79提供的regular_express。(我现在没有足够的声誉来投票!)

zombat的解决方案使用了太多的函数调用,我甚至简化了代码。我用PHP5.4把两个方案都运行了10万次,结果是这样的。

$str = 'Hello abc, have a nice day abc! abc!';
$pos = strpos($str, 'abc');
$str = substr_replace($str, '123', $pos, 3);

======1.85秒

$str = 'Hello abc, have a nice day abc! abc!';
$str = preg_replace('/abc/', '123', $str, 1);

======1.35秒

正如你所看到的。preg_replace的性能并不像很多人认为的那么差。所以我建议,如果你的正则表达式不复杂的话,就用经典的方案。

Hunter Wu 提问于2013-11-22
你的第一个片段是一个不公平的比较,因为它没有使用一个正确的实现。你没有为$pos检查false,所以当针不存在于干草堆中时,会损害输出。mickmackusa 2019-02-20
谢谢 @mickmackusa,你是对的。但这不是重点。我说这个代码被简化只是为了比较实现的效率。Hunter Wu 2019-02-23
这正是我的观点。你决不能进行不执行完全相同过程的基准比较。将苹果与半橘子相比较是没有用的。完全实现完整的非重合方法将使速度差异更加深刻。mickmackusa 2019-02-23
好吧,再次感谢。但我想要的是找到更好的实现方式,而不是让更多的差异变得深刻。Hunter Wu 2019-02-27
#14楼
得票数 2

为了扩展 zombat 的答案(我相信这是最好的答案),我创建了他的函数的递归版本,该函数接受 $limit 参数来指定要替换的出现次数。

function str_replace_limit($haystack, $needle, $replace, $limit, $start_pos = 0) {
    if ($limit <= 0) {
        return $haystack;
    } else {
        $pos = strpos($haystack,$needle,$start_pos);
        if ($pos !== false) {
            $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
            return str_replace_limit($newstring, $needle, $replace, $limit-1, $pos+strlen($replace));
        } else {
            return $haystack;
        }
    }
}
jtate 提问于2014-01-11
注意,对$start_pos没有质量检查,所以如果它超出了字符串的长度,这个函数将生成。Warning: strpos(): Offset not contained in string....当$start_pos超出长度时,此函数无法进行替换。失败的证明。3v4l.org/qGuVIR ...你的函数可以结合return $haystack条件,避免像这样声明一次性使用的变量。3v4l.org/Kdmqp然而,正如我在本页其他地方的评论中所说,我宁愿使用一个非常干净、直接、非递归的preg_replace()调用。mickmackusa 2019-02-20
是的,这样你就可以添加这一行 else 声明 $start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';ManojKiran Appathurai 2019-04-01
#15楼
得票数 2

对于一个字符串来说

$string = 'OOO.OOO.OOO.S';
$search = 'OOO';
$replace = 'B';

//replace ONLY FIRST occurance of "OOO" with "B"
    $string = substr_replace($string,$replace,0,strlen($search));
    //$string => B.OOO.OOO.S

//replace ONLY LAST occurance of "OOOO" with "B"
    $string = substr_replace($string,$replace,strrpos($string,$search),strlen($search)) 
    //$string => OOO.OOO.B.S

    //replace ONLY LAST occurance of "OOOO" with "B"
    $string = strrev(implode(strrev($replace),explode(strrev($search),strrev($string),2)))
    //$string => OOO.OOO.B.S

对于单一的字符

$string[strpos($string,$search)] = $replace;


//EXAMPLE

$string = 'O.O.O.O.S';
$search = 'O';
$replace = 'B';

//replace ONLY FIRST occurance of "O" with "B" 
    $string[strpos($string,$search)] = $replace;  
    //$string => B.O.O.O.S

//replace ONLY LAST occurance of "O" with "B" 
    $string[strrpos($string,$search)] = $replace; 
    // $string => B.O.O.B.S
oLinkWebDevelopment 提问于2014-03-08
oLinkWebDevelopment 修改于2014-03-08
当搜索字符串不在输入字符串的偏移量0时,第一个substr_replace()片段失败。失败的证明。3v4l.org/oIbRv 而两个substr_replace()技术在搜索值不存在时都会损坏输入字符串。失败的证明。3v4l.org/HmEml (最后一个技术中的所有rev调用都非常复杂/难看。)mickmackusa 2019-02-20
#16楼
得票数 2

补充一下人们所说的,记住,整个字符串是一个数组。

$string = "Lorem ipsum lá lá lá";

$string[0] = "B";

echo $string;

"Borem ipsum 啦啦啦"。

Leandro Castro 提问于2016-07-01
除非它包含多字节的字符......然后你的技术就会失败。多么不幸啊,你提供了一个包含á的输入字符串样本。失败的证明mickmackusa 2018-04-21
你可以用mb_strlen($subject) != strlen($subject)来验证你的string是否是一个多字节的字符串。alexandre-rousseau 2018-05-30
这篇帖子并不试图回答所问的问题。mickmackusa 2019-02-20
#17楼
得票数 1

这个函数在很大程度上受到了@renocor的回答的启发。 它使这个函数在多字节上是安全的。

function str_replace_limit($search, $replace, $string, $limit)
{
    $i = 0;
    $searchLength = mb_strlen($search);

    while(($pos = mb_strpos($string, $search)) !== false && $i < $limit)
    {
        $string = mb_substr_replace($string, $replace, $pos, $searchLength);
        $i += 1;
    }

    return $string;
}

function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
{
    $string = (array)$string;
    $encoding = is_null($encoding) ? mb_internal_encoding() : $encoding;
    $length = is_null($length) ? mb_strlen($string) - $start : $length;

    $string = array_map(function($str) use ($replacement, $start, $length, $encoding){

        $begin = mb_substr($str, 0, $start, $encoding);
        $end = mb_substr($str, ($start + $length), mb_strlen($str), $encoding);

        return $begin . $replacement . $end;

    }, $string);

    return ( count($string) === 1 ) ? $string[0] : $string;
}
blablabla 提问于2016-04-11
blablabla 修改于2016-04-11
#18楼
得票数 0

你可以使用这一点。

function str_replace_once($str_pattern, $str_replacement, $string){ 

        if (strpos($string, $str_pattern) !== false){ 
            $occurrence = strpos($string, $str_pattern); 
            return substr_replace($string, $str_replacement, strpos($string, $str_pattern), strlen($str_pattern)); 
        } 

        return $string; 
    } 

从php.net上找到了这个例子

使用方法。

$string = "Thiz iz an examplz";
var_dump(str_replace_once('z','Z', $string)); 

输出。

ThiZ iz an examplz

这可能会降低一点性能,但这是最简单的解决方案。

happyhardik 提问于2013-02-05
happyhardik 修改于2014-04-16
如果是这样的输出,那么意义何在?难道它不应该只把第一个小写字母 "z "替换成大写字母 "Z "吗?而不是把它们全部替换掉?我以为这就是我们在这里讨论的问题......Swivel 2014-03-11
我的错,它只会取代第一次出现的情况。已编辑。happyhardik 2014-04-16
同样的建议,巴斯在近3年前就已经提出过了(而且没有过分地称呼strpos())。被降权是因为它没有给页面增加任何新的价值。mickmackusa 2019-02-20
#19楼
得票数 0

For Loop解决方案

<?php
echo replaceFirstMatchedChar("&", "?", "/property/details&id=202&test=123#tab-6");

function replaceFirstMatchedChar($searchChar, $replaceChar, $str)
{
    for ($i = 0; $i < strlen($str); $i++) {

        if ($str[$i] == $searchChar) {
            $str[$i] = $replaceChar;
            break;
        }
    }
    return $str;
}
Black 提问于2019-10-29
#20楼
得票数 0

我想用preg来代替。它有一个LIMIT参数,你可以把它设置为1。

preg_replace (regex, subst, string, limit) // default is -1
Sergio Abreu 提问于2022-06-28
#21楼
得票数 -1
$str = "Hello there folks!"
$str_ex = explode("there, $str, 2);   //explodes $string just twice
                                      //outputs: array ("Hello ", " folks")
$str_final = implode("", $str_ex);    // glues above array together
                                      // outputs: str("Hello  folks")

还有一个额外的空间,但这并不重要,因为在我的案例中,它是用于后台脚本的。

not_loqan 提问于2017-11-27
这个技术是toomuchphp在2009年提供的!我把它降权了,因为你的帖子没有给研究人员增加新的价值。在发布答案之前,请确保你的解决方案对该页面是独一无二的,并为该页面增加价值。mickmackusa 2019-02-21
#22楼
得票数 -1

如果你的字符串不包含任何多字节的字符,如果你只想替换一个字符,你可以简单地使用strpos

这里有一个处理错误的函数

/**
 * Replace the first occurence of given string
 *
 * @param  string $search  a char to search in `$subject`
 * @param  string $replace a char to replace in `$subject`
 * @param  string $subject
 * @return string
 *
 * @throws InvalidArgumentException if `$search` or `$replace` are invalid or if `$subject` is a multibytes string
 */
function str_replace_first(string $search , string $replace , string $subject) : string {
    // check params
    if(strlen($replace) != 1 || strlen($search) != 1) {
        throw new InvalidArgumentException('$search & $replace must be char');
    }elseif(mb_strlen($subject) != strlen($subject)){
        throw new InvalidArgumentException('$subject is an multibytes string');
    }
    // search 
    $pos = strpos($subject, $search);
    if($pos === false) {
        // not found
        return $subject;
    }

    // replace
    $subject[$replace] = $subject;

    return $subject;
}
alexandre-rousseau 提问于2018-05-30
错误的偏移量 $subject[$replace] = $subject;, $replace在这个函数中是字符串L.C. Echo Chan 2021-06-08
#23楼
得票数 -2

这是我创建的一个简单的类,用来包装我们稍加修改的str_replace()函数。

我们的php::str_rreplace()函数也允许你进行反向的、有限的str_replace(),当试图只替换一个字符串的最后X个实例时,这将是非常方便的。

这些例子都使用了preg_replace()

<?php
class php {

    /**
    * str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
    *
    * @param string   $find
    * @param string   $replace
    * @param string   $subject
    * @param int      $replacement_limit | -1 to replace all references
    *
    * @return string
    */
    public static function str_replace($find, $replace, $subject, $replacement_limit = -1) {
        $find_pattern = str_replace('/', '\/', $find);
        return preg_replace('/' . $find_pattern . '/', $replace, $subject, $replacement_limit);
    }

    /**
    * str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
    *
    * @param string   $find
    * @param string   $replace
    * @param string   $subject
    * @param int      $replacement_limit | -1 to replace all references
    *
    * @return string
    */
    public static function str_rreplace($find, $replace, $subject, $replacement_limit = -1) {
        return strrev( self::str_replace(strrev($find), strrev($replace), strrev($subject), $replacement_limit) );
    }
}
Aran 提问于2014-05-07
你的帖子并没有为这个已经过度饱和的页面增加价值。你的regex解决方案在很多边缘情况下都失败了,因为你使用了不正确的工具来转义针形字符串中的字符。失败的证明。3v4l.org/dTdYK 来自2009的被大量支持和接受的答案已经显示了这个技术的正确执行。你的第二个方法没有回答所问的问题,而且已经由oLinkWebDevelopment提供了。mickmackusa 2019-02-21
标签