Usando str_replace in modo che agisca solo sulla prima partita?


Risposte:


346

Può essere fatto 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 è nel quarto parametro opzionale [Limite]. Dalla documentazione:

[Limite] - Il numero massimo possibile di sostituzioni per ciascun motivo in ciascuna stringa di soggetto. Il valore predefinito è -1 (nessun limite).


Tuttavia , vedi la risposta di zombat per un metodo più efficiente (circa 3-4 volte più veloce).


39
L'aspetto negativo di questo metodo è la penalità prestazionale delle espressioni regolari.
zombat,

27
Un altro aspetto negativo è che devi usare preg_quote () sul "ago" e sfuggire ai meta-caratteri $ e \ nella sostituzione.
Josh Davis,

32
Questo non riesce come soluzione generica a causa di cattivi problemi di fuga.
Jeremy Kauffman,

2
Troppo spesso le espressioni regolari vengono ignorate a causa della "performance", se la performance fosse la principale preoccupazione, non scriveremmo PHP! Qualcosa di diverso da '/' potrebbe essere usato per avvolgere lo schema, forse '~', il che contribuirebbe ad evitare il problema della fuga in una certa misura. Dipende da quali dati sono e da dove provengono.
ThomasRedstone,

1
Svantaggi prestazionali a parte: coloro che si lamentano di problemi di fuga hanno in mente qualcosa di specifico, oltre a potenziali bug preg_quote? Ad esempio, @ThomasRedstone teme che il delimitatore /potrebbe essere pericoloso se appare $from, ma per fortuna non lo è: è correttamente evaso a causa del preg_quotesecondo parametro (si può facilmente testarlo). Sarei interessato a conoscere questioni specifiche (che sarebbero gravi bug di sicurezza di PCRE nel mio libro).
MvanGeest

611

Non ne esiste una versione, ma la soluzione non è affatto confusa.

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

Abbastanza facile e salva la penalità prestazionale delle espressioni regolari.


Bonus: se si desidera sostituire l' ultima occorrenza, basta usare strrposal posto di strpos.


17
Può essere molto più veloce e utilizzerà meno memoria rispetto alle espressioni regolari. Non ho idea del perché qualcuno lo avrebbe votato in basso ...
Josh Davis,

12
Mi piace questo approccio, ma il codice ha un errore, l'ultimo parametro della chiamata substr_replace dovrebbe essere strlen ($ ago) anziché strlen ($ rimpiazzare) .. attenzione !!
Nelson,

È "confuso", nel senso che ci vuole molto più tempo per capire cosa sta succedendo. Anche se fosse un codice chiaro, non sarebbe stato detto che il codice ha un errore. Se è possibile commettere un errore in un frammento così piccolo, è già troppo confuso.
Camilo Martin,

9
Non sono d'accordo con @CamiloMartin per quanto riguarda il numero di righe rispetto alla possibilità di errori. Sebbene substr_replacesia una funzione un po 'ingombrante da utilizzare a causa di tutti i parametri, il vero problema è che fare manipolazione di stringhe con i numeri è talvolta complicato : devi stare attento a passare la variabile / offset giusta alle funzioni. In realtà andrei fino al punto di dire che il codice sopra è l'approccio più diretto e per me logico.
Alex,

1
Approccio geniale. Funziona perfettamente quando si sostituiscono i valori variabili che hanno caratteri regex riservati (quindi preg_replace è bear). Questo è semplice ed elegante.
Praesagus,

96

Modifica: entrambe le risposte sono state aggiornate e ora sono corrette. Lascerò la risposta poiché i tempi delle funzioni sono ancora utili.

Purtroppo le risposte di "zombat" e "too php" non sono corrette. Questa è una revisione della risposta postata da zombat (dato che non ho abbastanza reputazione per pubblicare un commento):

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

Nota lo strlen ($ ago), anziché lo strlen ($ rimpiazza). L'esempio di Zombat funzionerà correttamente solo se ago e sostituzione hanno la stessa lunghezza.

Ecco la stessa funzionalità in una funzione con la stessa firma dello str_replace di 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;
}

Questa è la risposta rivista di "troppo php":

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

Nota il 2 alla fine invece di 1. O nel formato della funzione:

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

Ho cronometrato le due funzioni e la prima è due volte più veloce quando non viene trovata alcuna corrispondenza. Hanno la stessa velocità quando viene trovata una corrispondenza.


Perché non generarlo come: str_replace_fLEX (misto $ s, misto $ r, int $ offset, int $ limite) dove la funzione sostituisce le occorrenze di $ limit a partire dalla corrispondenza $ offset (nth).
Adam Friedman,

Peccato che questo vale solo per le sostituzioni sensibili al maiuscolo / minuscolo.
andreszs

4
@Andrew stripos()in soccorso :-)
Gras Double

76

Mi chiedevo quale fosse il più veloce, quindi li ho testati tutti.

Di seguito troverai:

  • Un elenco completo di tutte le funzioni che sono state apportate in questa pagina
  • Test di benchmark per ogni contrubution (tempo medio di esecuzione oltre 10.000 esecuzioni)
  • Collegamenti a ciascuna risposta (per il codice completo)

Tutte le funzioni sono state testate con le stesse impostazioni:

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

Funzioni che sostituiscono solo la prima occorrenza di una stringa all'interno di una stringa:


Funzioni che sostituiscono solo l'ultima occorrenza di una stringa all'interno di una stringa:


Grazie per questo, di solito uso preg_replace in quanto è il più flessibile se in futuro sono necessari
ritocchi

@oLinkWebDevelopment Sarei interessato a vedere il tuo script di benchmark. Penso che potrebbe rivelarsi utile.
Dave Morton,

Il motivo per cui substr_replace()vince il risultato è semplice; perché è una funzione interna. Due funzioni interne e definite dall'utente che fanno la stessa cosa differiscono nelle prestazioni, perché quella interna viene eseguita in livelli inferiori. Quindi, perché no preg_match()? Le espressioni regolari sono quasi più lente di ogni funzione di manipolazione di stringhe interna, a causa della loro nazione di ricerca in una stringa più volte.
MAChitgarha,

1
Spero che il benchmark sul tuo "vincitore" ( substr_replace($string, $replace, 0, strlen($search));) non abbia semplicemente scritto quella statica 0. Parte della convoluzione delle soluzioni non regex è che devono "trovare" il punto di partenza prima di sapere dove sostituire.
Mickmackusa,

55

Sfortunatamente, non conosco alcuna funzione PHP in grado di farlo.
Puoi arrotolarlo abbastanza facilmente in questo modo:

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

Penso che questa sia la versione più golfistica di tutti - usando joininvece di implode.
Tito

return implode($replace, explode($find, $subject, $limit+1));per i numeri di sostituzione personalizzati
beppe9000,

7

Ho creato questa piccola funzione che sostituisce stringa su stringa (con distinzione tra maiuscole e minuscole) con limite, senza la necessità di Regexp. Funziona bene

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

Esempio di utilizzo:

$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

Anche se preferirei fare ===falseinvece di is_bool(essere più esplicito - sto dando questo pollice in su solo perché ha evitato la follia di RegExp ! ... e allo stesso tempo funziona e una soluzione pulita ...
jave.web

Preferire una preg_soluzione facilmente personalizzabile non è follia ma una preferenza personale. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);è piuttosto semplice da leggere per le persone che non temono la regex. Hai bisogno di una ricerca senza distinzione tra maiuscole e minuscole? Aggiungi idopo il delimitatore del modello finale. Hai bisogno del supporto Unicode / Multibyte? Aggiungi udopo il delimitatore del modello finale. Hai bisogno di supporto per i confini di parole? Aggiungi \bsu entrambi i lati della stringa di ricerca. Se non vuoi regex, non usare regex. Cavalli per i corsi, ma certamente non follia.
Mickmackusa,

3

Il modo più semplice sarebbe usare l'espressione regolare.

L'altro modo è trovare la posizione della stringa con strpos () e quindi un substr_replace ()

Ma andrei davvero per il RegExp.


Questo "suggerimento" è piuttosto vago / di basso valore rispetto ad altri post in questa pagina.
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));
}

Le risposte di solo codice hanno un valore basso su StackOverflow perché svolgono un cattivo lavoro di istruzione / responsabilizzazione di migliaia di futuri ricercatori.
Mickmackusa,

3

=> IL CODICE È STATO RIVEDUTO, quindi considera alcuni commenti troppo vecchi

E grazie a tutti per avermi aiutato a migliorarlo

Qualsiasi BUG, ​​per favore comunicami; Lo aggiusterò subito dopo

Quindi, andiamo per:

Sostituendo la prima 'o' in 'ea' per esempio:

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

//output: I leave you

La funzione:

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

Se $ questo ha ripetuto i caratteri come aaa vs aaaaaaaaa
Cristo,

Penso che dovrebbe essere substr($where,$b+strlen($this)), no substr($where,$b+1). E immagino che substr_replacesia più veloce.
Tito

Il codice è stato rivisto, ora funziona anche per stringhe lunghe
PYK

Questa soluzione non funziona come codificata. Prova: 3v4l.org/cMeZj E quando risolvi il problema di denominazione delle variabili, non funziona quando il valore di ricerca non viene trovato - danneggia la stringa di input. Prova: 3v4l.org/XHtfc
mickmackusa,

È giusto che qualcuno chieda di FISSARE il codice? @mickmackusa Puoi controllarlo di nuovo per favore?
PYK,

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

Questa è la stessa della prima risposta. Inoltre, si dovrebbe fare una preg_quotedi $findprima di utilizzarlo come espressione.
Emil Vikström,

questo è quello che ho usato, quindi l'ho votato. La prima risposta ha causato un conflitto con Drupal, deve aver sovrascritto una funzione di supporto di Drupal. Quindi ho appena preso il codice che era all'interno della funzione e l'ho usato in linea con il resto del codice ...
Dan Mantyla,

Questa risposta di solo codice fornisce consigli ridondanti sulla pagina (per non dire che manca preg_quote(). Questa risposta duplicata in ritardo può essere eliminata in modo sicuro dalla pagina perché i suoi consigli sono forniti dalla precedente e una risposta accettata con voto più elevato.
mickmackusa

2

Per espandere la risposta di @ renocor , ho scritto una funzione compatibile al 100% con le versioni precedenti str_replace(). Cioè, è possibile sostituire tutte le occorrenze di str_replace()con str_replace_limit()senza rovinare nulla fino, anche quelle che utilizzano le matrici per la $search, $replacee / o $subject.

La funzione potrebbe essere completamente autonoma, se si desidera sostituire la chiamata di funzione con ($string===strval(intval(strval($string)))), ma lo sconsiglio poiché valid_integer()è una funzione piuttosto utile quando si tratta di numeri interi forniti come stringhe.

Nota: quando possibile, str_replace_limit()utilizzerà str_replace()invece, in modo che tutte le chiamate a str_replace()possano essere sostituite str_replace_limit()senza preoccuparsi di un colpo alla performance.

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 sostituzioni - bbcbbc

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

1 rimpiazzi - bbcabc

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

2 sostituzioni - bbcbbc

Funzione

<?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
un po 'gonfio se me lo chiedi. Anche ciò che odio di più in questa soluzione è la gestione degli errori. Si rompe lo script se si passano valori errati. Pensi che sembri professionale ma non lo è, invece di un errore produce invece un avviso o un avviso. Meglio saltare le stronzate, restituire false invece o null e non usare mai una backtrace in una funzione come questa. La soluzione migliore è che il programmatore possa decidere cosa fare quando l'output è errato / imprevisto.
Codebeat

@Erwinus Questo utilizza E_USER_WARNINGdappertutto, che è un avvertimento , non un errore . Il backtrace è estremamente utile per scoprire quale codice sta trasmettendo i dati non validi alla funzione in primo luogo (che è assolutamente necessario per rintracciare i bug in produzione). Per quanto riguarda la restituzione $subjectanziché false/ nullo il lancio di un errore, è stata semplicemente una scelta personale per il mio caso d'uso. Per abbinare str_replace()la funzionalità, usare errori fatali catchable sarebbe la scommessa migliore (come str_replace()quando si fornisce una chiusura per i primi due argomenti).
0b10011,

Ah, non ho notato l'E_USER_WARNING che stai usando, scusami per quello. Il problema con la restituzione dell'argomento è che non puoi mai vedere che c'era qualcosa di sbagliato, al di fuori della funzione. Detto questo, la funzione può essere la metà delle dimensioni se lo fai in modo più intelligente (è possibile). In secondo luogo, i commenti vanno bene quando spiegano qualcosa di complesso ma non molto utile per cose semplici come aumentare un valore. Nel complesso, penso che sia inutile enorme. Inoltre, l'utilizzo di avvisi in un ambiente di produzione può essere un problema di sicurezza quando si utilizza questo codice su un server che non sopprime i messaggi di runtime per impostazione predefinita (registri).
Codebeat

@Erwinus Ero prolisso quando si trattava dei commenti perché alcune persone non capiscono la lingua così come altre, e i commenti possono sempre essere rimossi da coloro che la comprendono. Se conosci un modo migliore per ottenere lo stesso risultato finale per tutti i casi limite, modifica la risposta. E se il tuo ambiente di produzione non elimina i messaggi di errore, hai un problema più grande di questa funzione;)
0b10011

TL; DR Questo frammento è così gonfio che non riesco a immaginare di sceglierlo su una funzione regex (odio lo scorrimento). Se si desidera contare le sostituzioni effettuate, esiste un parametro per questo in preg_replace(). Inoltre, preg_replace()/ regex offre la gestione dei limiti di parola (se desiderabile) - qualcosa che le funzioni non regex non forniranno in modo elegante.
Mickmackusa,

2

Secondo il mio risultato del test, vorrei votare quello regular_express fornito da karim79. (Non ho abbastanza reputazione per votarla adesso!)

La soluzione di zombat utilizza troppe chiamate di funzioni, ho anche semplificato i codici. Sto usando PHP 5.4 per eseguire entrambe le soluzioni per 100.000 volte, ed ecco il risultato:

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

==> 1,85 sec

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

==> 1,35 sec

Come potete vedere. Le prestazioni di preg_replace non sono così male come pensano molte persone. Quindi suggerirei la soluzione di classe se il tuo espresso regolare non è complicato.


Il tuo primo frammento è un confronto ingiusto perché non riesce a utilizzare un'implementazione corretta. Non sta verificando $posper false, in modo che quando l'ago non esiste nel pagliaio, danneggerà l'uscita.
Mickmackusa,

Grazie @mickmackusa, hai ragione. Ma non è questo il punto. Ho detto che questo codice è semplificato solo per confrontare l'efficienza delle implementazioni.
Hunter Wu,

Questo è esattamente il mio punto. Non devi mai effettuare confronti di benchmark che non eseguono lo stesso identico processo. Il confronto tra mele e mezze arance non è utile. L'implementazione completa dell'approccio completo non regex renderà più profonda la differenza di velocità.
Mickmackusa,

Bene, grazie ancora. Ma quello che voglio è trovare la migliore implementazione, non fare più profonde differenze.
Hunter Wu,

2

Per espandere la risposta di zombat (che credo sia la migliore risposta), ho creato una versione ricorsiva della sua funzione che accetta un $limitparametro per specificare quante occorrenze si desidera sostituire.

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

Nota, non v'è alcun controllo di qualità su $start_pos, quindi se è oltre la lunghezza della stringa, questa funzione genera: Warning: strpos(): Offset not contained in string.... Questa funzione non riesce a effettuare una sostituzione quando $start_posè oltre la lunghezza. Prova di fallimento: 3v4l.org/qGuVIR ... La tua funzione può combinare le return $haystackcondizioni ed evitare di dichiarare variabili monouso come questa: 3v4l.org/Kdmqp Tuttavia, come ho detto nei commenti altrove in questa pagina, preferirei usa una preg_replace()chiamata molto pulita, diretta, non ricorsiva .
Mickmackusa,

sì, in modo da poter aggiungere questa riga elselinement$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A

2

Per una stringa

$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

Per un singolo personaggio

$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

Il primo frammento substr_replace () ha esito negativo quando la stringa di ricerca non si trova sull'offset 0 della stringa di input. Prova di errore: 3v4l.org/oIbRv ed entrambe le substr_replace()tecniche danneggiano la stringa di input quando il valore di ricerca non è presente. Prova di fallimento: 3v4l.org/HmEml (E quell'ultima tecnica con tutte le revchiamate è seriamente contorta / dura per gli occhi.)
Mickmackusa,

2

A complemento di ciò che la gente ha detto, ricorda che l'intera stringa è un array:

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

$string[0] = "B";

echo $string;

"Borem ipsum lá lá lá"


3
A meno che non contenga caratteri multibyte ... e quindi la tua tecnica fallisce. Che peccato che tu abbia offerto una stringa di input di esempio contenente á. Dimostrazione di fallimento
mickmackusa,

Puoi verificare se la tua stringè una stringa multibyte usandomb_strlen($subject) != strlen($subject)
RousseauAlexandre

Questo post non tenta di rispondere alla domanda che viene posta.
Mickmackusa,

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

Usando substr_replace possiamo sostituire la ricorrenza del primo carattere solo nella stringa. come è ripetuto più volte ma solo alla prima posizione dobbiamo sostituire e con?


1

Questa funzione è fortemente ispirata dalla risposta di @renocor. Rende sicura la funzione multi byte.

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

Puoi usare questo:

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

Trovato questo esempio da php.net

Uso:

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

Produzione:

ThiZ iz an examplz

Ciò può ridurre leggermente le prestazioni, ma la soluzione più semplice.


Se questo è l'output di qual è il punto? Non dovrebbe sostituire solo la prima "z" minuscola con una "Z" maiuscola? Invece di sostituirli tutti? Pensavo fosse quello di cui stavamo parlando qui ...
Swivel

Mio male, sostituirà solo la prima occorrenza. Modificato.
happyhardik,

Lo stesso consiglio era già stato offerto da Bas quasi 3 anni prima (e senza chiamare eccessivamente strpos()). Sottovalutato perché non aggiunge alcun nuovo valore alla pagina.
Mickmackusa,

0

Se la stringa non contiene caratteri multibyte e se si desidera sostituire solo un carattere, è possibile utilizzare semplicemente strpos

Qui una funzione che gestisce gli errori

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

Per la soluzione Loop

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

Ecco una semplice classe che ho creato per racchiudere le nostre funzioni str_replace () leggermente modificate .

La nostra funzione php :: str_rreplace () consente anche di eseguire str_replace () inverso e limitato, che può essere molto utile quando si tenta di sostituire solo le istanze X finali di una stringa.

Questi esempi usano entrambi 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) );
    }
}

Il tuo post non aggiunge valore a questa pagina già superata. La tua soluzione regex fallisce in molti casi di frangia perché hai usato lo strumento errato per sfuggire ai caratteri nella stringa dell'ago. Prova di fallimento: 3v4l.org/dTdYK La risposta ampiamente votata e accettata dal 2009 mostra già la corretta esecuzione di questa tecnica. Il tuo secondo metodo non risponde alla domanda posta ed è già stato fornito da 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")

C'è un ulteriore spazio aggiuntivo, ma nel mio caso non importava come lo script backgound.


Questa tecnica è stata fornita da Toomuchphp nel 2009 ! Ho annullato il voto perché il tuo post non aggiunge nuovo valore ai ricercatori. Assicurati, prima di pubblicare una risposta, che la tua soluzione sia unica per la pagina e aggiunga valore alla pagina.
Mickmackusa,

-3

questa è la mia prima risposta qui, spero di farlo correttamente. Perché non utilizzare il quarto argomento della funzione str_replace per questo problema?

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

conteggio: se superato, verrà impostato sul numero di sostituzioni eseguite.


modifica: questa risposta è errata, poiché il quarto parametro di str_replace è una variabile a cui viene assegnato il numero di sostituzioni eseguite. Ciò non è coerente con preg_replace , che ha un quarto parametro $limite un quinto parametro &$count.


Il quarto argomento verrà impostato da str_replace () sul numero di sostituzioni effettuate. Ecco perché ricevi un errore quando passi un numero intero e non una variabile.
Arminrosu,

-6

È facile trovare una soluzione per sostituire solo la prima o la prima coppia di istanze (fornendo il valore di conteggio). Non ci sono molte soluzioni per sostituire l'ultima o l'ultima coppia di istanze.

Forse qualcosa come str_replace ($ find, $ replace, $ subject, -3) dovrebbe sostituire le ultime tre istanze.

Comunque, solo un suggerimento.


4
Perché rispondere a una domanda con un suggerimento quando una risposta è stata accettata due anni prima ?!
mbinette,

Inoltre -3 non funzionerà come parametro, poiché il quarto parametro viene emesso e non è un parametro di input. Sarebbe meglio se testassi ciò che proponi, invece di pubblicare codice che si arresta in modo anomalo.
Ghostwriter78,

Sì, questo è sbagliato, tuttavia, perché ottengo un blocco dello schermo vuoto, quando lo provo? Ho fatto il solito error_reporting (E_ALL); ini_set ("display_errors", 1); Errore schermo ancora vuoto.
Doug Cassidy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.