使用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
)不一致。