本文翻译自:Using str_replace so that it only acts on the first match?
I want a version of str_replace()
that only replaces the first occurrence of $search
in the $subject
. 我想要一个只替换$subject
第一次出现的$search
的str_replace()
版本。 Is there an easy solution to this, or do I need a hacky solution? 是否有一个简单的解决方案,还是我需要一个hacky解决方案?
#1楼
参考:https://stackoom.com/question/5Fsj/使用str-replace使其仅作用于第一个匹配项
#2楼
$string = 'this is my world, not my world';
$find = 'world';
$replace = 'farm';
$result = preg_replace("/$find/",$replace,$string,1);
echo $result;
#3楼
To expand on @renocor's answer , I've written a function that is 100% backward-compatible with str_replace()
. 为了扩展@renocor的答案 ,我编写了一个与str_replace()
100%向后兼容的函数。 That is, you can replace all occurrences of str_replace()
with str_replace_limit()
without messing anything up, even those using arrays for the $search
, $replace
, and/or $subject
. 也就是说,你可以替换所有出现的str_replace()
与str_replace_limit()
不搞乱任何东西,即使是那些使用数组为$search
, $replace
,和/或$subject
。
The function could be completely self-contained, if you wanted to replace the function call with ($string===strval(intval(strval($string))))
, but I'd recommend against it since valid_integer()
is a rather useful function when dealing with integers provided as strings. 如果您想将函数调用替换为($string===strval(intval(strval($string))))
,该函数可能是完全独立的,但我建议您反对它,因为valid_integer()
是一个处理作为字符串提供的整数时相当有用的函数。
Note: Whenever possible, str_replace_limit()
will use str_replace()
instead, so all calls to str_replace()
can be replaced with str_replace_limit()
without worrying about a hit to performance. 注意: 只要有可能, 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 replacements -- bbcbbc 2个替换-bbcbbc
$limit = 1; // Limit of 1
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;
1 replacements -- bbcabc 1替补-bbcabc
$limit = 10; // Limit of 10
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;
2 replacements -- bbcbbc 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;
}
}
#4楼
Can be done with preg_replace : 可以用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'
The magic is in the optional fourth parameter [Limit]. 不可思议的地方是可选的第四个参数[Limit]。 From the documentation: 从文档中:
[Limit] - The maximum possible replacements for each pattern in each subject string. [限制]-每个主题字符串中每个模式的最大可能替换量。 Defaults to -1 (no limit). 默认为-1(无限制)。
Though, see zombat's answer for a more efficient method (roughly, 3-4x faster). 不过,请参阅zombat的答案以获取更有效的方法(大约快3-4倍)。
#5楼
The easiest way would be to use regular expression. 最简单的方法是使用正则表达式。
The other way is to find the position of the string with strpos() and then an substr_replace() 另一种方法是使用strpos()然后是substr_replace()查找字符串的位置
But i would really go for the RegExp. 但是我真的会去RegExp。
#6楼
There's no version of it, but the solution isn't hacky at all. 没有它的版本,但是该解决方案一点也不hacky。
$pos = strpos($haystack, $needle);
if ($pos !== false) {
$newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
}
Pretty easy, and saves the performance penalty of regular expressions. 非常简单,并且节省了正则表达式的性能损失。
Bonus: If you want to replace last occurrence, just use strrpos
in place of strpos
. 奖励:如果要替换最后一次出现,只需使用strrpos
代替strpos
。