我想要一个仅替换str_replace()中第一次出现的$search$subject版本。是否有一个简单的解决方案,还是我需要一个hacky解决方案?

评论

您可能会发现s($ subject)-> replaceFirst($ search)和s($ subject)-> replaceFirstIgnoreCase($ search)很有帮助,在此独立库中可以找到。

您可能想知道$ count参数的用途是什么,好是用于OUTPUT-执行了多少次替换-没有限制,所以这是一个完全有效的问题。这可能会与其他语言混淆,因为例如Python的str.replace确实使用count参数作为限制参数。

#1 楼

可以使用preg_replace来完成:

function str_replace_first($from, $to, $content)
{
    $from = '/'.preg_quote($from, '/').'/';

    return preg_replace($from, $to, $content, 1);
}

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


神奇之处在于可选的第四个参数[Limit]。从文档中:


[限制]-每个
主题字符串中每个模式的最大可能替换数。默认值为-1(无限制)。不过,请参阅zombat的答案以获取更有效的方法(大约快3-4倍)。

评论


这种方法的缺点是正则表达式的性能下降。

– zombat
09年8月10日在2:42

另一个缺点是,您必须在“ needle”上使用preg_quote()并在替换中转义元字符$和\。

–乔什·戴维斯(Josh Davis)
09年8月10日在2:53

由于讨厌的转义问题,这无法作为通用解决方案。

–杰里米·考夫曼(Jeremy Kauffman)
2011年7月9日下午0:39

正则表达式经常由于“性能”而被忽略,如果性能是首要考虑因素,我们就不会编写PHP!可以使用'/'以外的其他东西来包装模式,也许是'〜',这在某种程度上有助于避免转义问题。这取决于数据是什么,以及它来自何处。

– ThomasRedstone
2015年12月11日23:26



除了性能方面的缺点外,那些抱怨逃避问题的人除了preg_quote中潜在的错误之外,还有什么特别的主意吗?例如,@ ThomasRedstone担心定界符/如果出现在$ from中可能会很危险,但幸运的是并非如此:由于preg_quote的第二个参数(可以轻松测试),它可以正确地转义。我希望听到有关特定问题的信息(这是我书中严重的PCRE安全错误)。

– MvanGeest
17年1月31日在2:30



#2 楼

它没有版本,但是解决方案一点也不荒谬。

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


非常简单,并且节省了正则表达式的性能损失。

奖励:如果要替换最后一次出现,只需使用strrpos代替strpos

评论


与正则表达式相比,速度可能更快,并且占用的内存更少。不知道为什么有人会投票否决...

–乔什·戴维斯(Josh Davis)
09年8月10日在2:54

我喜欢这种方法,但是代码有错误,substr_replace调用的最后一个参数应该是strlen($ needle)而不是strlen($ replace)..请注意!

–尼尔森
2010-09-21在11:47

从某种意义上来说,这是“ hacky”,需要花费更多的时间才能确定正在发生的事情。同样,如果代码清晰,就不会提到代码有错误。如果有可能在这么小的代码段中犯错误,那就太过分了。

–卡米洛·马丁(Camilo Martin)
13年11月12日在18:01

对于行数与错误可能性,我不同意@CamiloMartin。由于所有参数的存在,substr_replace函数使用起来有些笨拙,但真正的问题是,用数字进行字符串操作有时很棘手-您必须小心地将正确的变量/偏移量传递给函数。实际上,我什至可以说上面的代码是最直接的方法,对我而言,也是合乎逻辑的方法。

– Alex
2014年4月22日在15:21

精采的方法。替换其中保留了正则表达式字符的变量值时,它可以完美工作(因此preg_replace为bear)。这是简单而优雅的。

– Praesagus
2014年11月7日,下午2:55

#3 楼

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

不幸的是,“ zombat”和“太多的php”的答案不正确。这是对zombat发布的答案的修订(因为我没有足够的声誉来发表评论):

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


请注意strlen($ needle) of strlen($ replace)。 Zombat的示例仅在针和替换的长度相同时才能正常工作。

函数中具有与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));
}


我为这两个函数计时,当找不到匹配项时,第一个函数的速度快一倍。找到比赛时它们的速度相同。

评论


为什么不将其归类为:str_replace_flexible(mixed $ s,mixed $ r,int $ offset,int $ limit),其中函数替换从$ offset(nth)匹配处开始的$ limit出现。

–亚当·弗里德曼
2014年5月23日19:09

不幸的是,这仅适用于区分大小写的替换。

– andreszs
2015年3月19日23:02

@Andrew stripos()来解救:-)

–Gras Double
16-2-13在19:23



#4 楼

我想知道哪个是最快的,所以我对它们全部进行了测试。

您将在下面找到:


已提供的所有功能的完整列表到此页面上
每个构造的基准测试(平均执行时间超过10,000次)
每个答案的链接(完整的代码)


所有功能均经过测试具有相同的设置:

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



仅替换字符串中第一次出现的函数:



substr_replace($string, $replace, 0, strlen($search));

[CONTRIBUTED BY] => zombat
[OOO.OOO.OOO.S] => B.OOO.OOO.S
[AVERAGE TIME] => 0.0000062883
[SLOWER BY] => FASTEST



replace_first($search, $replace, $string);

[CONTRIBUTED BY] => too much php
[OOO.OOO.OOO.S] => B.OOO.OOO.S
[AVERAGE TIME] => 0.0000073902
[SLOWER BY] => 17.52%



preg_replace($search, $replace, $string, 1);

[CONTRIBUTED BY] => karim79
[OOO.OOO.OOO.S] => B.OOO.OOO.S
[AVERAGE TIME] => 0.0000077519
[SLOWER BY] => 23.27%



str_replace_once($search, $replace, $string);

[CONTRIBUTED BY] => happyhardik
[OOO.OOO.OOO.S] => B.OOO.OOO.S
[AVERAGE TIME] => 0.0000082286
[SLOWER BY] => 30.86%



str_replace_limit($search, $replace, $string, $count, 1);

[CONTRIBUTED BY] => bfrohs - expanded renocor
[OOO.OOO.OOO.S] => B.OOO.OOO.S
[AVERAGE TIME] => 0.0000083342
[SLOWER BY] => 32.54%



str_replace_limit($search, $replace, $string, 1);

[CONTRIBUTED BY] => renocor
[OOO.OOO.OOO.S] => B.OOO.OOO.S
[AVERAGE TIME] => 0.0000093116
[SLOWER BY] => 48.08%



str_replace_limit($string, $search, $replace, 1, 0);

[CONTRIBUTED BY] => jayoaK
[OOO.OOO.OOO.S] => B.OOO.OOO.S
[AVERAGE TIME] => 0.0000093862
[SLOWER BY] => 49.26%




仅替换字符串中最后出现的函数:


substr_replace($string, $replace, strrpos($string, $search), strlen($search));

[CONTRIBUTED BY] => oLinkSoftware - modified zombat
[OOO.OOO.OOO.S] => OOO.OOO.B.S
[AVERAGE TIME] => 0.0000068083
[SLOWER BY] => FASTEST



strrev(implode(strrev($replace), explode(strrev($search), strrev($string), 2)));

[CONTRIBUTED BY] => oLinkSoftware
[OOO.OOO.OOO.S] => OOO.OOO.B.S
[AVERAGE TIME] => 0.0000084460
[SLOWER BY] => 24.05%




评论


感谢这一点,我通常使用preg_replace,因为如果在大多数情况下需要将来的调整,它是最灵活的,它的速度减慢了27%并不重要

– zzapper
16年1月28日在14:32

@oLinkWebDevelopment我会对查看您的基准脚本感兴趣。我认为这可能是有用的。

–戴夫·莫顿(Dave Morton)
17年4月7日在16:28

substr_replace()赢得结果的原因很简单;因为它是内部功能。内部执行功能和用户定义功能这两个功能在性能上有所不同,因为内部功能在较低层运行。那么,为什么不preg_match()?正则表达式几乎比每个内部字符串操作函数都要慢,因为它们会多次搜索字符串。

– MAChitgarha
18/09/21在6:13

我希望您的“获胜者”(substr_replace($ string,$ replace,0,strlen($ search));)上的基准测试不仅要写成静态0。非正则表达式解决方案的部分卷积在于它们需要在知道替换位置之前先“找到”起点。

– mickmackusa
19-2-20在3:43

#5 楼

不幸的是,我不知道有任何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));
}


评论


我认为这是所有功能中最高级的版本-使用连接而不是内爆。

– Titus
17年11月21日在8:57



return implode($ replace,explode($ find,$ subject,$ limit + 1));用于自定义替换编号

– beppe9000
19/12/7在23:08



#6 楼

我创建了这个小函数,用Reg替换了string上的字符串(区分大小写)并带有限制。

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


评论


虽然我宁愿=== false而不是is_bool(更明确-我之所以竖起大拇指,是因为它避免了RegExp疯狂!...,同时又可以正常工作并且是干净的解决方案。 。

–jave.web
16-3-14在15:03



选择易于定制的preg_解决方案不是疯狂,而是个人喜好。返回preg_replace('/'。preg_quote($ search,'/').'/',$ replace,$ content,1);对于不担心正则表达式的人来说,它很容易阅读。需要不区分大小写的搜索吗?在结束模式定界符后添加i。需要unicode / multibyte支持吗?在结束模式定界符后添加u。需要字边界支持?在搜索字符串的两边添加\ b。如果您不希望使用正则表达式,请不要使用正则表达式。马为课程,但肯定不是疯狂的。

– mickmackusa
19年2月20日,下午3:52

#7 楼

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

另一种方法是使用strpos()然后使用substr_replace()查找字符串的位置,但是我会真的很喜欢RegExp。

评论


与此页面上的其他帖子相比,此“提示”相当模糊/低价值。

– mickmackusa
19年2月20日,下午3:53

#8 楼

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));
}


评论


纯代码的答案在StackOverflow上是低价值的,因为它们在教育/授权成千上万的未来研究人员方面做得很差。

– mickmackusa
19年2月20日,下午3:54

#9 楼

=>修改了代码,因此请考虑一些过时的注释


并感谢大家帮助我改善此问题

任何错误,请与我交流;我会在


之后立即修复它,所以,让我们开始吧:

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

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

//output: I leave you


功能:

function str_replace_first($a,$b,$s)
         {
         $w=strpos($s,$a);
         if($w===false)return $s;
         return substr($s,0,$w).$b.substr($s,$w+strlen($a));
         }


评论


如果$ this重复了像aaa vs aaaaaaaaa的字符,则失败

–克里斯托
16年6月13日在8:09

我认为应该是substr($ where,$ b + strlen($ this)),而不是substr($ where,$ b + 1)。而且我猜想substr_replace更快。

– Titus
17年11月21日9:00



代码已修改,现在甚至可以用于长字符串

– PYK
19年2月12日在17:10

此解决方案无法按编码方式工作。证明:3v4l.org/cMeZj而且,当您解决了变量命名问题时,如果未找到搜索值,则它不起作用-会损坏输入字符串。证明:3v4l.org/XHtfc

– mickmackusa
19年2月20日在4:01

有人要求修复代码是否公平? @mickmackusa您能再检查一次吗?

– PYK
19年2月20日在15:04

#10 楼

$string = 'this is my world, not my world';
$find = 'world';
$replace = 'farm';
$result = preg_replace("/$find/",$replace,$string,1);
echo $result;


评论


这与第一个答案相同。此外,在将其用作表达式之前,应执行$ find的preg_quote。

–埃米尔·维克斯特伦(EmilVikström)
2012年6月21日22:37

这就是我所使用的,所以我投票赞成。第一个答案引起了与Drupal的冲突,它必须已经覆盖了Drupal助手功能。所以我只使用了函数内部的代码,并将其与其余代码内联使用...

–丹·曼蒂拉(Dan Mantyla)
17年9月19日在21:23

这个仅代码的答案在页面上提供了多余的建议(更不用说它缺少preg_quote()。可以从页面上安全地清除此较晚的重复答案,因为它的建议是由较早使用的,较高级别的接受答案提供的。

– mickmackusa
19年2月20日,下午3:56

#11 楼

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

该函数可以是完全独立的,如果您想将函数调用替换为($string===strval(intval(strval($string)))),但我建议不要使用它,因为valid_integer()是处理以字符串形式提供的整数时相当有用的函数。

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

用法

<?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


功能

<?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;

    }

}


评论


如果你问我,我有点肿。同样,我最讨厌这种解决方案的是错误处理。如果传递不正确的值,它将破坏脚本。您认为它看起来很专业,但事实并非如此,不是错误而是发出通知或警告。更好的方法是跳过废话,返回false或返回null,并且永远不要在这样的函数中使用backtrace。最好的解决方案是,程序员可以决定输出错误/意外时应采取的措施。

– Codebeat
13年7月16日在20:40



@Erwinus在整个过程中都使用E_USER_WARNING,这是警告,而不是错误。回溯非常有用,它首先可以找出哪些代码将无效数据传递给函数(这对于跟踪生产中的错误是绝对必要的)。至于返回$ subject而不是false / null或引发错误,那只是我的用例的个人选择。为了匹配str_replace()的功能,最好使用可捕获的致命错误(就像str_replace()为前两个参数提供闭包时一样)。

– 0b10011
13年7月17日在3:03

嗯,没有注意到您正在使用E_USER_WARNING,对此感到抱歉。返回主题的问题是,在函数外部您永远看不到任何错误。也就是说,如果您更聪明地使用该功能,则其大小可以减小一半(有可能)。其次,在解释复杂的事物时使用注释很好,但对于简单的事物(例如增加价值)却不是很有用。总的来说,我认为这是没有必要的。此外,当您在不默认禁止运行时消息(日志)的服务器上使用此代码时,在生产环境中使用警告也可能是安全问题。

– Codebeat
13年7月17日在3:47



@Erwinus在评论时,我很冗长,因为有些人和其他人一样不懂该语言,并且那些理解它的人总是可以删除评论。如果您知道在所有极端情况下都能获得相同最终结果的更好方法,请编辑答案。而且,如果您的生产环境不能抑制错误消息,那么您遇到的问题比此函数还大;)

– 0b10011
13年7月17日在3:58

TL; DR这个代码片段太肿,以至于我无法想象通过正则表达式功能选择它(我讨厌滚动)。如果您要计算已进行的替换,则preg_replace()中有一个参数。此外,preg_replace()/ regex提供了字边界处理(如果需要的话)-非regex函数无法优雅地提供这些功能。

– mickmackusa
19年2月20日在4:17

#12 楼

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

zombat的解决方案使用了太多的函数调用,甚至简化了代码。我正在使用PHP 5.4来运行两种解决方案100,000次,结果如下:

$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的性能并不像许多人想象的那么差。因此,如果您的正则表达式不复杂,我建议您采用经典的解决方案。

评论


您的第一个摘要是不公平的比较,因为它没有使用正确的实现。您无需检查$ pos是否为假,因此,当大海捞针中不存在针时,它将损坏输出。

– mickmackusa
19年2月20日在6:04

谢谢@mickmackusa,你是对的。但这不是重点。我说过,简化此代码只是为了比较实现效率。

–吴猎人
19年2月23日在3:46

这正是我的意思。您绝不能进行不完全相同的过程的基准比较。将苹果与橘子进行比较是没有用的。完全实施完整的非正则表达式方法将使速度差异更加深刻。

– mickmackusa
19年2月23日在4:01



好,再次感谢。但是我想要的是找到更好的实现,而不是使更多的改变深刻。

–吴猎人
19-2-27在3:13

#13 楼

为了扩展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;
        }
    }
}


评论


请注意,没有对$ start_pos进行质量检查,因此,如果超出字符串长度,则此函数将生成:警告:strpos():字符串中不包含偏移量...。当$ start_pos时,此函数无法替换超出长度。故障证明:3v4l.org/qGuVIR ...您的函数可以结合返回的$ haystack条件,并避免像这样声明一次性变量:3v4l.org/Kdmqp但是,正如我在本页其他地方的评论中所述,我宁愿使用一个非常干净,直接,非递归的preg_replace()调用。

– mickmackusa
19-2-20在4:33



是的,以便您可以添加此行else statement $ start_pos> strlen($ haystack)吗? $ start_pos = strlen($ haystack):'';

–Manojkiran.A
19年4月1日在6:54



#14 楼

对于字符串

$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


评论


当搜索字符串不在输入字符串的偏移量0处时,第一个substr_replace()代码段失败。故障证明:3v4l.org/oIbRv当不存在搜索值时,两种substr_replace()技术都会损坏输入字符串。失败证明:3v4l.org/HmEml(最后一次涉及所有rev调用的技术都被严重地弄乱了/难以察觉。)

– mickmackusa
19年2月20日在4:08

#15 楼

要补充人们所说的内容,请记住整个字符串是一个数组:

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

$string[0] = "B";

echo $string;


“ Borem ipsumlálálá”

评论


除非它包含多字节字符...然后您的技术会失败。不幸的是,您提供了包含á的示例输入字符串。失败的证明

– mickmackusa
18年4月21日在6:41

您可以使用mb_strlen($ subject)!= strlen($ subject)来验证您的字符串是否为多字节字符串。

–卢梭·亚历山大(RousseauAlexandre)
18年5月30日在9:05

这篇文章并不试图回答所提出的问题。

– mickmackusa
19年2月20日,下午3:58

#16 楼

$str = "/property/details&id=202&test=123#tab-6p";
$position = strpos($str,"&");
echo substr_replace($str,"?",$position,1);


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

#17 楼

该功能受到@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;
}


#18 楼

您可以使用:

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


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

评论


如果那是输出,那又有什么意义呢?它不应该只用大写字母“ Z”代替第一个小写字母“ z”吗?而不是全部替换?我以为那就是我们在这里谈论的...

–旋转
2014年3月11日在6:08

我不好,它只会代替第一次出现。编辑。

–happyhardik
14年4月16日在10:39

Bas已经在近3年前提供了相同的建议(并且没有过多调用strpos())。拒绝投票,因为它不会为页面添加任何新值。

– mickmackusa
19年2月20日在8:59

#19 楼

如果您的字符串不包含任何多字节字符,并且只想替换一个字符,则可以简单地使用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;
}


#20 楼

对于环路解决方案

<?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;
}


#21 楼

这是我创建的一个简单类,用于包装经过稍微修改的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) );
    }
}


评论


您的信息并没有增加饱和页面的价值。您的正则表达式解决方案在很多情况下都失败了,因为您使用了不正确的工具来转义针串中的字符。失败证明:3v4l.org/dTdYK 2009年获得广泛好评并得到广泛接受的答案已经表明了该技术的正确执行。您的第二种方法不能回答所问的问题,oLinkWebDevelopment已经提供了。

– mickmackusa
19年2月21日在6:20

#22 楼

$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")


还有一个额外的空间,但这并不重要,因为在我的情况下,它是用于backgound脚本的。

评论


该技术由toomuchphp于2009年提供!我之所以投票,是因为您的帖子对研究人员没有任何新的价值。在发布答案之前,请确保您的解决方案对页面而言是唯一的,并且可以为页面增加价值。

– mickmackusa
19年2月21日在5:18