使用str_replace使其仅在第一个匹配项上起作用?


Answers:


346

可以用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倍)。


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

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

32
由于讨厌的转义问题,这无法作为通用解决方案。
杰里米·考夫曼

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

1
除了性能方面的缺点外,那些抱怨逃避问题的人除了潜在的错误外,是否还有其他具体的想法preg_quote?例如,@ThomasRedstone担心/如果在中出现分隔符可能会很危险$from,但幸运的是,分隔符没有:由于preg_quote的第二个参数而被正确地转义了(可以很容易地对其进行测试)。我想听听有关特定问题的信息(这是我书中严重的PCRE安全错误)。
MvanGeest '17

610

没有它的版本,但是该解决方案一点也不hacky。

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

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


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


17
与正则表达式相比,速度可以更快,并且占用的内存更少。不知道为什么有人会投票否决……
乔什·戴维斯

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

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

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

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

96

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

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

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

注意strlen($ needle),而不是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出现。
亚当·弗里德曼

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

4
@安德鲁(Andrew)stripos()进行救援:-)
Double)

76

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

在下面您会发现:

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

所有功能均使用相同的设置进行了测试:

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

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


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


感谢这个,我一般使用的preg_replace,因为它是最灵活的,如果需要在大多数情况下,27%较慢的调整未来不会是显著
zzapper

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

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

1
我希望您的“赢家”(substr_replace($string, $replace, 0, strlen($search));)的基准不仅仅写成static 0。非正则表达式解决方案的部分复杂之处在于,它们需要在找到替换位置之前先“找到”起点。
mickmackusa

55

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

我认为这是所有产品中使用率最高的版本join而不是implode
泰特斯

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

7

我创建了这个函数,该函数无需使用Regexp即可将string上的string(区分大小写)替换为limit。它工作正常。

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

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

3

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

另一种方法是使用strpos()然后是substr_replace()查找字符串的位置

但是我真的会去RegExp。


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

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

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

3

=>已修改代码,因此请考虑一些注释过旧

感谢大家帮助我改善这一点

任何错误,请与我沟通;我会马上解决

所以,让我们去:

例如,将第一个“ 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之类的字符,则失败
Cristo

我认为应该是substr($where,$b+strlen($this)),不是substr($where,$b+1)。我想那substr_replace会更快。
泰特斯(Titus)

修改了代码,现在它甚至适用于长字符串
PYK

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

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

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

这与第一个答案相同。此外,你应该做preg_quote$find使用它作为一个表达式之前。
EmilVikström2012年

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

这只代码的答案在页面上提供了冗余的意见(更何况这是缺乏preg_quote()这晚重复的答案可以从网页被安全地清除,因为它的建议是由早期的提供,以及更高的upvoted接受的答案。
mickmackusa

2

为了扩展@renocor的答案,我编写了一个函数,该函数100%向后兼容str_replace()。也就是说,你可以替换所有的出现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;

    }

}

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

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

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

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

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

2

根据我的测试结果,我想对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的性能并不像许多人想象的那么差。因此,如果您的常规快递并不复杂,我建议您使用经典的解决方案。


您的第一个摘要是不公平的比较,因为它没有使用正确的实现。你不检查$posfalse,所以当针不会在草堆存在,它会伤害输出。
mickmackusa

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

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

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

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

请注意,没有质量检查$start_pos,因此,如果超出字符串长度,此函数将生成:Warning: strpos(): Offset not contained in string...$start_pos超出长度时,此功能无法替换。故障证明:3v4l.org/qGuVIR ...您的函数可以组合return $haystack条件并避免声明一次性变量,例如:3v4l.org/Kdmqp 但是,正如我在本页其他地方的评论中所述,我宁愿使用非常干净,直接,非递归的preg_replace()调用。
mickmackusa

是的,以便您可以添加此行else声明$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A

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

当搜索字符串不在输入字符串的偏移量0处时,第一个substr_replace()代码段失败。故障证明:3v4l.org/oIbRvsubstr_replace()当不存在搜索值时,这两种技术都会损坏输入字符串。失败的证明:3v4l.org/HmEml (而且所有rev电话的最后一种技术都被严重
扭曲

2

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

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

$string[0] = "B";

echo $string;

“ Borem ipsumlálálá”


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

您可以string使用mb_strlen($subject) != strlen($subject)
RousseauAlexandre,

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

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

使用substr_replace,我们只能替换字符串中第一个字符的出现。因为&被重复多次,但只有在第一位置,我们才必须用&替换?


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

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

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


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

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

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

0

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

0

对于循环解决方案

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

-1

这是我创建的一个简单类,用于包装经过稍微修改的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 全副upvoted和接受的答案2009年已经具备了这一技术的正确执行。您的第二种方法不能回答所问的问题,oLinkWebDevelopment已经提供了。
mickmackusa

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

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


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

-3

这是我在这里的第一个答案,我希望正确地做。为什么不使用str_replace函数的第四个参数解决此问题?

mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

count:如果通过,它将被设置为执行的替换次数。


编辑:这个答案是错误的,因为str_replace的第四个参数是一个变量,该变量被分配完成的替换次数。这与具有第4个参数和第5个参数的preg_replace不一致。$limit&$count


第四个参数将由str_replace()设置为已进行替换的数量。这就是为什么在传递整数而不是变量时会出错的原因。
arminrosu 2014年

-6

很容易找到仅替换前两个实例的解决方案(通过提供计数值)。没有很多解决方案可以代替最后或最后几个实例。

也许像str_replace($ find,$ replace,$ subject,-3)之类的东西应该替换最后三个实例。

无论如何,只是一个建议。


4
为什么在两年前已经接受答案,并提出建议?!
mbinette 2012年

另外-3不能用作参数,因为第4个参数是输出而不是输入参数。如果您测试建议的内容,而不是发布崩溃的代码,那将更好。
Ghostwriter78

是的,这是错误的,但是,为什么在尝试时出现黑屏崩溃?我做了通常的error_reporting(E_ALL); ini_set(“ display_errors”,1); 仍然黑屏错误。
Doug Cassidy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.