¿Usa str_replace para que solo actúe en la primera coincidencia?


Respuestas:


346

Se puede hacer con 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'

La magia está en el cuarto parámetro opcional [Límite]. De la documentación:

[Límite]: el máximo de reemplazos posibles para cada patrón en cada cadena de asunto. El valor predeterminado es -1 (sin límite).


Sin embargo, vea la respuesta de zombat para un método más eficiente (aproximadamente, 3-4 veces más rápido).


39
La desventaja de este método es la penalización de rendimiento de las expresiones regulares.
zombat

27
Otro inconveniente es que debe usar preg_quote () en la "aguja" y escapar de los metacaracteres $ y \ en el reemplazo.
Josh Davis el

32
Esto falla como una solución genérica debido a problemas de escape desagradables.
Jeremy Kauffman

2
Con demasiada frecuencia, las expresiones regulares se descartan debido al "rendimiento", si el rendimiento fuera la principal preocupación, ¡no estaríamos escribiendo PHP! Algo más que '/' podría usarse para ajustar el patrón, tal vez '~', lo que ayudaría a evitar el problema de escape en algún grado. Depende de cuáles sean los datos y de dónde provienen.
ThomasRedstone

1
Dejando a un lado las desventajas del rendimiento: ¿tienen en mente aquellos que se quejan de problemas de escape, además de posibles errores preg_quote? Por ejemplo, a @ThomasRedstone le preocupa que el delimitador /pueda ser peligroso si aparece $from, pero afortunadamente no lo es: se escapó correctamente debido al preg_quotesegundo parámetro (uno puede probarlo fácilmente). Me interesaría saber sobre problemas específicos (que serían serios errores de seguridad PCRE en mi libro).
MvanGeest

611

No hay una versión de este, pero la solución no es nada hacky.

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

Bastante fácil y ahorra la penalización de rendimiento de las expresiones regulares.


Bonificación: si desea reemplazar la última ocurrencia, simplemente use strrposen lugar de strpos.


17
Puede ser mucho más rápido y usará menos memoria que las expresiones regulares. Ni idea de por qué alguien podría votar que abajo ...
Josh Davis

12
Me gusta este enfoque, pero el código tiene un error, el último parámetro de la llamada substr_replace debe ser strlen ($ needle) en lugar de strlen ($ replace) ... ¡¡¡cuidado con eso !!
Nelson

Es "hacky" en el sentido de que lleva mucho más tiempo darse cuenta de lo que está sucediendo. Además, si fuera un código claro, no se habría mencionado que el código tiene un error. Si es posible cometer un error en un fragmento tan pequeño, ya es demasiado hacky.
Camilo Martin

99
No estoy de acuerdo con @CamiloMartin con respecto a la cantidad de líneas frente a la posibilidad de errores. Si bien substr_replacees una función algo difícil de manejar debido a todos los parámetros, el problema real es que hacer la manipulación de cadenas por números a veces es complicado : debe tener cuidado de pasar la variable / desplazamiento correcto a las funciones. De hecho, iría tan lejos como para decir que el código anterior es el enfoque más directo y, para mí, lógico.
Alex

1
Enfoque brillante. Funciona perfectamente al reemplazar valores variables que tienen caracteres de expresiones regulares reservados en ellos (por lo que preg_replace es bear). Esto es sencillo y elegante.
Praesagus

96

Editar: ambas respuestas se han actualizado y ahora son correctas. Dejaré la respuesta ya que los tiempos de la función siguen siendo útiles.

Lamentablemente, las respuestas de 'zombat' y 'demasiado php' no son correctas. Esta es una revisión de la respuesta publicada por zombat (ya que no tengo suficiente reputación para publicar un comentario):

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

Tenga en cuenta el strlen ($ aguja), en lugar de strlen ($ replace). El ejemplo de Zombat solo funcionará correctamente si la aguja y el reemplazo tienen la misma longitud.

Aquí está la misma funcionalidad en una función con la misma firma que el propio str_replace de PHP:

function str_replace_first($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos !== false) {
        return substr_replace($subject, $replace, $pos, strlen($search));
    }
    return $subject;
}

Esta es la respuesta revisada de 'demasiado php':

implode($replace, explode($search, $subject, 2));

Tenga en cuenta el 2 al final en lugar de 1. O en formato de función:

function str_replace_first($search, $replace, $subject) {
    return implode($replace, explode($search, $subject, 2));
}

Tomé el tiempo de las dos funciones y la primera es el doble de rápido cuando no se encuentra ninguna coincidencia. Tienen la misma velocidad cuando se encuentra una coincidencia.


¿Por qué no generizar esto como: str_replace_flexible ($ s mixtos, $ r mixtos, int $ offset, int $ limit) donde la función reemplaza las ocurrencias de $ limit comenzando en la coincidencia $ offset (nth).
Adam Friedman

Lástima que esto solo se aplica a reemplazos sensibles a mayúsculas y minúsculas.
andreszs

44
@Andrew stripos()al rescate :-)
Gras Double

76

Me preguntaba cuál era el más rápido, así que los probé a todos.

Abajo encontrarás:

  • Una lista completa de todas las funciones que se han contribuido en esta página
  • Pruebas de referencia para cada contribución (tiempo de ejecución promedio de más de 10,000 ejecuciones)
  • Enlaces a cada respuesta (para el código completo)

Todas las funciones se probaron con la misma configuración:

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

Funciones que solo reemplazan la primera aparición de una cadena dentro de una cadena:


Funciones que solo reemplazan la última aparición de una cadena dentro de una cadena:


Gracias por esto, por lo general utilizan preg_replace, ya que es el más flexible si el futuro pellizco se requiere en la mayoría de los casos el 27% más lentas no va a ser significativo
zzapper

@oLinkWebDevelopment Me interesaría ver su script de referencia. Creo que podría resultar útil.
Dave Morton el

La razón por la que substr_replace()gana el resultado es simple; porque es una función interna. Dos funciones internas y definidas por el usuario que hacen lo mismo difieren en rendimiento, porque la interna se ejecuta en capas inferiores. Entonces, ¿por qué no preg_match()? Las expresiones regulares son casi más lentas que todas las funciones internas de manipulación de cadenas, debido a su nación de búsqueda en una cadena varias veces.
MAChitgarha

1
Espero que el punto de referencia en su "ganador" ( substr_replace($string, $replace, 0, strlen($search));) no haya escrito simplemente esa estática 0. Parte de la convolución de las soluciones que no son expresiones regulares es que necesitan "encontrar" el punto de partida antes de saber dónde reemplazar.
mickmackusa

55

Desafortunadamente, no conozco ninguna función PHP que pueda hacer esto.
Puede rodar el suyo con bastante facilidad de esta manera:

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

Creo que esta es la versión más elegante de todas, usando en joinlugar de implode.
Titus

return implode($replace, explode($find, $subject, $limit+1));para números de reemplazo personalizados
beppe9000

7

Creé esta pequeña función que reemplaza cadena en cadena (distingue entre mayúsculas y minúsculas) con límite, sin la necesidad de Regexp. Funciona bien.

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

Ejemplo de uso:

$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

Aunque preferiría hacerlo en ===falselugar de is_bool(ser más explícito, ¡doy este visto bueno porque ha evitado la locura de RegExp ! ... y al mismo tiempo funciona y es una solución limpia ...
jave.web

Preferir una preg_solución fácilmente personalizable no es una locura, sino una preferencia personal. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);es bastante simple de leer para las personas que no temen a las expresiones regulares. ¿Necesita una búsqueda que no distinga entre mayúsculas y minúsculas? Agregue idespués del delimitador de patrón final. ¿Necesita soporte unicode / multibyte? Agregue udespués del delimitador de patrón final. ¿Necesita soporte de límite de palabra? Agregue \ba ambos lados de su cadena de búsqueda. Si no quieres regex, no uses regex. Caballos para cursos, pero ciertamente no locura.
mickmackusa

3

La forma más fácil sería usar expresiones regulares.

La otra forma es encontrar la posición de la cadena con strpos () y luego un substr_replace ()

Pero realmente iría por el RegExp.


Esta "pista" es bastante vaga / de bajo valor en comparación con otras publicaciones en esta página.
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));
}

Las respuestas de solo código son de poco valor en StackOverflow porque hacen un mal trabajo al educar / empoderar a miles de futuros investigadores.
mickmackusa

3

=> EL CÓDIGO FUE REVISADO, así que considera algunos comentarios demasiado viejos

Y gracias a todos por ayudarme a mejorar eso

Cualquier error, por favor comuníqueme; Lo arreglaré justo después

Entonces, vamos por:

Reemplazando la primera 'o' por 'ea' por ejemplo:

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

//output: I leave you

La función:

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

Falla si $ esto ha repetido caracteres como aaa vs aaaaaaaaa
Cristo

Creo que debería ser substr($where,$b+strlen($this)), no substr($where,$b+1). Y supongo que eso substr_replacees más rápido.
Titus

El código fue revisado, ahora funciona incluso para cadenas largas
PYK

Esta solución no funciona como codificada. Prueba: 3v4l.org/cMeZj Y cuando soluciona el problema de nomenclatura variable, no funciona cuando no se encuentra el valor de búsqueda, daña la cadena de entrada. Prueba: 3v4l.org/XHtfc
mickmackusa

¿Es justo que alguien solicite CORREGIR el código? @mickmackusa ¿Puedes comprobar eso de nuevo por favor?
PYK

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

Esto es lo mismo que la primera respuesta. Además, usted debe hacer una preg_quotede $findantes de utilizarlo como una expresión.
Emil Vikström

esto es lo que usé, así que lo voté. La primera respuesta causó un conflicto con Drupal, debe haber sobrescrito una función auxiliar de Drupal. Así que tomé el código que estaba dentro de la función y lo usé en línea con el resto del código ...
Dan Mantyla

Esta respuesta de sólo código proporciona asesoramiento redundante en la página (por no hablar de que se carece de preg_quote()esta respuesta duplicado tarde se puede purgar de forma segura desde la página debido a que su consejo es proporcionada por el anterior, y la respuesta aceptada mayor upvoted..
mickmackusa

2

Para ampliar la respuesta de @ renocor , he escrito una función que es 100% compatible con versiones anteriores str_replace(). Es decir, se puede reemplazar todas las ocurrencias de str_replace()la str_replace_limit()sin jugar a nada, incluso aquellos que utilizan matrices para el $search, $replacey / o$subject .

La función podría ser completamente autónoma, si quisiera reemplazar la llamada a la función ($string===strval(intval(strval($string)))), pero lo recomendaría, ya que valid_integer()es una función bastante útil cuando se trata de enteros proporcionados como cadenas.

Nota: Siempre que sea posible, str_replace_limit()se utilizará str_replace()en su lugar, por lo que todas las llamadas a str_replace()se pueden reemplazarstr_replace_limit() sin preocuparse por un golpe en el rendimiento.

Uso

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

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

1 reemplazos - bbcabc

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

2 reemplazos - bbcbbc

Función

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

    }

}

44
un poco hinchado si me preguntas. Además, lo que más 'odio' en esta solución es el manejo de errores. Rompe el script si pasa valores incorrectos. Cree que parece profesional, pero no lo es, en lugar de un error, produzca un aviso o advertencia. Es mejor omitir las tonterías, devolver false en su lugar o nulo y nunca usar una traza inversa en una función como esta. La mejor solución es que el programador puede decidir qué hacer cuando la salida es incorrecta / inesperada.
Codebeat

@Erwinus Esto se usa en E_USER_WARNINGtodo momento, lo cual es una advertencia , no un error . La traza inversa es extremadamente útil para averiguar qué código está pasando los datos no válidos a la función en primer lugar (que es absolutamente necesario para rastrear errores en la producción). En cuanto a regresar en $subjectlugar de false/ nullo arrojar un error, esa fue simplemente una elección personal para mi caso de uso. Para igualar str_replace()la funcionalidad, el uso de errores fatales detectables sería la mejor opción (como lo str_replace()es cuando se proporciona un cierre para los dos primeros argumentos).
0b10011

Ah, no me di cuenta sobre el E_USER_WARNING que estás usando, lo siento. El problema con la devolución del tema es que nunca se puede ver que haya algo mal, fuera de la función. Dicho esto, la función puede ser la mitad del tamaño si lo hace de manera más inteligente (es posible). Segundo, los comentarios están bien cuando explican algo complejo pero no muy útil para cosas simples como aumentar un valor. En general, creo que es innecesario enorme. Además, el uso de advertencias en un entorno de producción puede ser un problema de seguridad cuando utiliza este código en un servidor que no suprime los mensajes de tiempo de ejecución de forma predeterminada (registros).
Codebeat

@ Erwinus Fui detallado cuando se trataba de los comentarios porque algunas personas no entienden el idioma tan bien como otras, y los comentarios siempre pueden ser eliminados por aquellos que sí lo entienden. Si conoce una mejor manera de obtener el mismo resultado final para todos los casos límite, edite la respuesta. Y si su entorno de producción no suprime los mensajes de error, tiene un problema mayor que esta función;)
0b10011

TL; DR Este fragmento está tan hinchado que no puedo imaginar elegirlo en lugar de una función regex (odio el desplazamiento). Si desea contar los reemplazos realizados, hay un parámetro para eso en preg_replace(). Además, preg_replace()/ regex ofrece manejo de límites de palabras (si lo desea), algo que las funciones que no son expresiones regulares no proporcionarán con elegancia.
mickmackusa

2

De acuerdo con el resultado de mi prueba, me gustaría votar el regular_express proporcionado por karim79. (¡No tengo suficiente reputación para votarlo ahora!)

La solución de zombat usa demasiadas llamadas a funciones, incluso simplifico los códigos. Estoy usando PHP 5.4 para ejecutar ambas soluciones 100,000 veces, y aquí está el resultado:

$str = 'Hello abc, have a nice day abc! abc!';
$pos = strpos($str, 'abc');
$str = substr_replace($str, '123', $pos, 3);

==> 1.85 segundos

$str = 'Hello abc, have a nice day abc! abc!';
$str = preg_replace('/abc/', '123', $str, 1);

==> 1.35 segundos

Como puedes ver. El rendimiento de preg_replace no es tan malo como mucha gente piensa. Por lo tanto, sugeriría la solución con clase si su expreso regular no es complicado.


Su primer fragmento es una comparación injusta porque no puede utilizar una implementación correcta. No está $posbuscando false, por lo que cuando la aguja no existe en el pajar, dañará la salida.
mickmackusa

Gracias @mickmackusa, tienes razón. Pero ese no es el punto. Dije que este código se simplifica solo para comparar la eficiencia de las implementaciones.
Hunter Wu

Ese es exactamente mi punto. Nunca debe hacer comparaciones de referencia que no realicen exactamente el mismo proceso. Comparar manzanas con medias naranjas no es útil. Implementar completamente el enfoque completo sin expresiones regulares hará que la diferencia de velocidad sea más profunda.
mickmackusa

Bueno, gracias de nuevo. Pero lo que quiero es encontrar la mejor implementación, no hacer una diferencia más profunda.
Hunter Wu

2

Para ampliar la respuesta de zombat (que creo que es la mejor respuesta), creé una versión recursiva de su función que incluye un $limitparámetro para especificar cuántas ocurrencias desea reemplazar.

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

Tenga en cuenta, no hay control de calidad de $start_pos, por lo que si está más allá de la longitud de la cadena, esta función generará: Warning: strpos(): Offset not contained in string.... Esta función no puede hacer un reemplazo cuando $start_posestá más allá de la longitud. Prueba de falla: 3v4l.org/qGuVIR ... Su función puede combinar las return $haystackcondiciones y evitar declarar variables de un solo uso como esta: 3v4l.org/Kdmqp Sin embargo, como he dicho en comentarios en otra parte de esta página, prefiero use una preg_replace()llamada muy limpia, directa y no recursiva .
mickmackusa

sí, para que pueda agregar esta elsedeclaración de línea$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran. A

2

Para una cuerda

$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

Para un solo personaje

$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

El primer fragmento substr_replace () falla cuando la cadena de búsqueda no está en el desplazamiento 0 de la cadena de entrada. Prueba de falla: 3v4l.org/oIbRv Y ambas substr_replace()técnicas dañan la cadena de entrada cuando el valor de búsqueda no está presente. Prueba de falla: 3v4l.org/HmEml (Y esa última técnica con todas las revllamadas es seriamente complicada / dura para los ojos.)
mickmackusa

2

Complementando lo que dijo la gente, recuerde que toda la cadena es una matriz:

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

$string[0] = "B";

echo $string;

"Borem ipsum lá lá lá"


3
A menos que contenga caracteres multibyte ... y luego su técnica falle. Qué desafortunado que ofreció una cadena de entrada de muestra que contiene á. Demostración de fracaso
mickmackusa

Puede verificar si su stringes una cadena multibyte usandomb_strlen($subject) != strlen($subject)
RousseauAlexandre

Esta publicación no intenta responder la pregunta que se hace.
mickmackusa

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

Usando substr_replace podemos reemplazar la aparición del primer carácter solo en cadena. como & se repite varias veces pero solo en la primera posición tenemos que reemplazar & con?


1

Esta función está fuertemente inspirada en la respuesta de @renocor. Hace que la función multi byte sea segura.

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

Puedes usar esto:

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

Encontré este ejemplo de php.net

Uso:

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

Salida:

ThiZ iz an examplz

Esto puede reducir un poco el rendimiento, pero es la solución más fácil.


Si ese es el resultado, ¿cuál es el punto? ¿No debería reemplazar solo la primera "z" minúscula con una "Z" mayúscula? En lugar de reemplazarlos a todos? Pensé que eso era de lo que estábamos hablando aquí ...
Swivel

Lo malo es que solo reemplazará la primera vez. Editado
happyhardik

Bas ya había ofrecido este mismo consejo casi 3 años antes (y sin llamar en exceso strpos()). Vota abajo porque no agrega ningún valor nuevo a la página.
mickmackusa

0

Si su cadena no contiene caracteres multibyte y si desea reemplazar solo un carácter, simplemente puede usar strpos

Aquí una función que maneja errores

/**
 * 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

Para solución de bucle

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

Aquí hay una clase simple que creé para envolver nuestro str_replace () ligeramente modificado funciones .

Nuestra función php :: str_rreplace () también le permite llevar a cabo un str_replace () inverso y limitado que puede ser muy útil cuando se trata de reemplazar solo las instancias X finales de una cadena.

Estos ejemplos usan 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) );
    }
}

Su publicación no agrega valor a esto ya sobre la página saturada. Su solución regex falla en muchos casos marginales porque usó la herramienta incorrecta para escapar de los caracteres en la cadena de la aguja. Prueba de falla: 3v4l.org/dTdYK La respuesta altamente votada y aceptada de 2009 ya muestra la ejecución adecuada de esta técnica. Su segundo método no responde a la pregunta formulada y oLinkWebDevelopment ya lo proporcionó.
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")

Hay un espacio adicional más, pero en mi caso no importó, ya que era para el script de fondo.


¡Esta técnica fue proporcionada por toomuchphp en 2009 ! He votado negativamente porque su publicación no agrega ningún valor nuevo a los investigadores. Antes de publicar una respuesta, asegúrese de que su solución es exclusiva de la página y agrega valor a la página.
mickmackusa

-3

Esta es mi primera respuesta aquí, espero hacerlo correctamente. ¿Por qué no usar el cuarto argumento de la función str_replace para este problema?

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

recuento: si se aprueba, se establecerá en el número de reemplazos realizados.


editar: esta respuesta es incorrecta, porque el cuarto parámetro de str_replace es una variable a la que se le asigna el número de reemplazos realizados. Esto es inconsistente con preg_replace , que tiene un cuarto parámetro $limity un quinto parámetro &$count.


Str_replace () establecerá los cuartos argumentos para el número de reemplazos que se hicieron. Es por eso que obtiene un error cuando le pasa un número entero y no una variable.
arminrosu

-6

Es fácil encontrar una solución para reemplazar solo la primera o la primera pareja de instancias (dando el valor de conteo). No hay muchas soluciones para reemplazar el último o el último par de instancias.

Tal vez algo como str_replace ($ find, $ replace, $ subject, -3) debería reemplazar las últimas tres instancias.

De todos modos, solo una sugerencia.


44
¿Por qué responder una pregunta con una sugerencia cuando una respuesta ha sido aceptada dos años antes?
mbinette

Además, -3 no funcionará como parámetro, porque el cuarto parámetro se emite y no se ingresa. Sería mejor si prueba lo que propone, en lugar de publicar código que se bloquea.
Ghostwriter78

Sí, esto está mal, sin embargo, ¿por qué me cuelgo la pantalla en blanco cuando lo intento? Hice el error_reporting habitual (E_ALL); ini_set ("display_errors", 1); Todavía error de pantalla en blanco.
Doug Cassidy
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.