使用str_replace,以便它只作用于第一场比赛?

我想要一个str_replace()版本,它只replace$subject第一次出现的$search 。 有没有一个简单的解决scheme,或者我需要一个hacky解决scheme?

可以用preg_replace完成:

 function str_replace_first($from, $to, $subject) { $from = '/'.preg_quote($from, '/').'/'; return preg_replace($from, $to, $subject, 1); } echo str_replace_first('abc', '123', 'abcdef abcdef abcdef'); // outputs '123def abcdef abcdef' 

魔法在可选的第四个参数[Limit]中。 从文档:

[限制] – 每个主题string中每个模式的最大可能replace。 默认为-1(没有限制)。

虽然,看到zombat的答案更有效的方法(粗略,3-4倍更快)。

没有它的版本,但是解决scheme根本不算黑客。

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

很容易,并且节省了正则expression式的性能损失。

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

编辑:两个答案已经更新,现在是正确的。 我将离开答案,因为function时序仍然有用。

不幸的是,'zombat'和'太多的PHP'的答案是不正确的。 这是一个修改的答案zombat张贴(因为我没有足够的声誉发表评论):

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

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

下面是与PHP自己的str_replace具有相同签名的函数中的相同function:

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

我计时这两个function,第一个是没有find匹配时的两倍。 当发现一场比赛时,他们的速度是一样的。

我想知道哪一个是最快的,所以我testing了他们。

下面你会发现:

  • 所有已经被贡献到这个页面的function的综合列表
  • 每个控制的基准testing(平均执行时间超过10,000次)
  • 链接到每个答案(完整的代码)

所有function都使用相同的设置进行testing:

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

只能replacestring中第一个出现的string的函数:

  • 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% 

只能replacestring中最后一个string的函数:

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

     [CONTRIBUTED BY] => oLinkSoftware - modified zombat [OOO.OOO.OOO.S] => OOO.OOO.BS [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.BS [AVERAGE TIME] => 0.0000084460 [SLOWER BY] => 24.05% 

不幸的是,我不知道任何可以做到这一点的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)); } 

我创build了这个函数,用stringreplacestring(区分大小写),而不需要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 

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

另一种方法是用strpos()findstring的位置,然后用substr_replace()

但是我真的会去RegExp。

 $string = 'this is my world, not my world'; $find = 'world'; $replace = 'farm'; $result = preg_replace("/$find/",$replace,$string,1); echo $result; 
 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)); } 

将第一个“o”replace为“ea” ,例如:

 $a='I love you'; echo str_replace_first('o','ea',$a); //output: I leave you 

function:

 function str_replace_first($this,$that,$where) { $b=strpos($where,$this); return substr($where,0,$b).$that.substr($where,$b+1); } 

为了扩展@ renocor的答案 ,我写了一个与str_replace()向后兼容的函数。 也就是说,你可以用str_replace()replace所有出现的str_replace()而不会搞乱任何东西,甚至是那些使用$search$replace和/ or $subject数组。

如果你想用($string===strval(intval(strval($string))))来replace函数调用,那么函数可以是完全独立的,但是我build议不valid_integer() ,因为valid_integer()是一个处理作为string提供的整数时非常有用的函数。

注意:只要有可能, str_replace_limit()就会使用str_replace() ,所有对str_replace()调用都可以用str_replace_limit()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; 

2replace – bbcbbc

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

1replace – bbcabc

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

2replace – bbcbbc

function

 <?php /** * Checks if $string is a valid integer. Integers provided as strings (eg '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 (eg 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; } } 

在PHP中,您可以迭代string的字母或访问字母,因为可以访问数组的元素。

只返回string的第一个字母的片段:

 $word[0] = str_replace($find, $replace, $word[0]); 

您还可以使用大括号访问每个字母。 我使用下面的代码来replace小写的波兰重音字符为大写字母。

 $word{0} = str_replace( array('ą', 'ż', 'ś', 'ź', 'ę', 'ć', 'ł', 'ó', 'ń'), array('Ą', 'Ż', 'Ś', 'Ź', 'Ę', 'Ć', 'Ł', 'Ó', 'Ń'), $word{0} ); 

根据我的testing结果,我想投票karim79提供的regular_express。 (我现在没有足够的声望去投票!)

来自zombat的解决scheme使用了太多的函数调用,甚至简化了代码。 我使用PHP 5.4来运行两次解决scheme10万次,结果如下:

 $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的performance并不像许多人想象的那么糟糕。 所以,如果你的正规快递不复杂,我会build议优雅的解决scheme。

为了扩大zombat的答案(我相信这是最好的答案),我创build了一个函数的recursion版本,它接受$limit参数来指定要replace的出现次数。

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

对于一个string

 $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.BS //replace ONLY LAST occurance of "OOOO" with "B" $string = strrev(implode(strrev($replace),explode(strrev($search),strrev($string),2))) //$string => OOO.OOO.BS 

对于单个字符

 $string[strpos($string,$search)] = $replace; //EXAMPLE $string = 'OOOOS'; $search = 'O'; $replace = 'B'; //replace ONLY FIRST occurance of "O" with "B" $string[strpos($string,$search)] = $replace; //$string => BOOOS //replace ONLY LAST occurance of "O" with "B" $string[strrpos($string,$search)] = $replace; // $string => BOOBS 

作为人们所说的补充,记住整个string是一个数组:

 $string = "Lorem ipsum lá lá lá"; $string[0] = "B"; echo $string; 

“Borem ipsumlálálá”

你可以使用这个:

 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.netfind这个例子

用法:

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

输出:

 ThiZ iz an examplz 

这可能会降低性能,但最简单的解决scheme。

这个函数深受@renocor的回答启发。 它使function多字节安全。

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

这是我创build的一个简单的类,用于包装稍微修改的str_replace()函数。

我们的php :: str_rreplace()函数还允许你执行一个反向的,有限的str_replace(),当试图只replace一个string的最后一个X实例时,它可以非常方便。

这些例子都使用preg_replace() 。

 <?php class php { /** * str_replace() from the end of a string that can also be limited eg 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 eg 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) ); } } 

它很容易find一个解决scheme,以取代只有第一个或前两个实例(通过给计数值)。 没有太多解决scheme来取代最后一个或最后一个实例。

也许像str_replace($ find,$ replace,$ subject,-3)应该replace最后三个实例。

反正只是一个build议。

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

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

计数:如果通过,则将被设置为执行的replace次数。

编辑:这个答案是错误的,因为str_replace的第四个参数是一个variables被赋值的replace次数。 这与preg_replace (第四个参数$limit和第五个参数&$count )不一致。