Verwenden Sie str_replace, damit es nur beim ersten Match wirkt?


Antworten:


345

Kann mit preg_replace durchgeführt werden :

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'

Die Magie liegt im optionalen vierten Parameter [Limit]. Aus der Dokumentation:

[Limit] - Die maximal mögliche Ersetzung für jedes Muster in jeder Betreffzeichenfolge. Der Standardwert ist -1 (keine Begrenzung).


Obwohl finden Sie zombat Antwort für eine effizientere Methode (grob, 3-4x schneller).


39
Der Nachteil dieser Methode ist die Leistungsbeeinträchtigung durch reguläre Ausdrücke.
Zombat

27
Ein weiterer Nachteil ist, dass Sie preg_quote () für die "Nadel" verwenden und die Metazeichen $ und \ im Ersatz maskieren müssen.
Josh Davis

32
Dies schlägt als generische Lösung aufgrund böser Probleme fehl.
Jeremy Kauffman

2
Viel zu oft werden reguläre Ausdrücke aufgrund von "Leistung" verworfen. Wenn Leistung das Hauptanliegen wäre, würden wir kein PHP schreiben! Etwas anderes als '/' könnte verwendet werden, um das Muster zu verpacken, vielleicht '~', was helfen würde, das entweichende Problem bis zu einem gewissen Grad zu vermeiden. Es kommt darauf an, was die Daten sind und woher sie stammen.
Thomas Redstone

1
Abgesehen von Leistungseinbußen - haben diejenigen, die sich über Probleme beschweren, etwas Besonderes im Sinn, abgesehen von potenziellen Fehlern preg_quote? Zum Beispiel befürchtet @ThomasRedstone, dass das Trennzeichen /gefährlich sein könnte, wenn es in angezeigt wird $from, aber zum Glück nicht: Es wird aufgrund des preg_quotezweiten Parameters ordnungsgemäß maskiert (das kann man leicht testen). Ich würde gerne etwas über bestimmte Probleme erfahren (die in meinem Buch schwerwiegende PCRE-Sicherheitslücken darstellen würden).
MvanGeest

609

Es gibt keine Version davon, aber die Lösung ist überhaupt nicht hackig.

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

Ziemlich einfach und spart die Leistungseinbußen bei regulären Ausdrücken.


Bonus: Wenn Sie das letzte Vorkommen ersetzen möchten , verwenden Sie es einfach strrposanstelle von strpos.


17
Kann viel schneller sein und benötigt weniger Speicher als reguläre Ausdrücke. Keine Ahnung, warum jemand das ablehnen würde ...
Josh Davis

12
Ich mag diesen Ansatz, aber der Code hat einen Fehler. Der letzte Parameter des Aufrufs von substr_replace sollte strlen ($adel) anstelle von strlen ($ replace) sein. Bitte beachten Sie dies!
Nelson

Es ist "hacky" in dem Sinne, dass es erheblich länger dauert, um herauszufinden, was los ist. Auch wenn es sich um klaren Code handelte, wäre nicht erwähnt worden, dass der Code einen Fehler aufweist. Wenn es möglich ist, in einem so kleinen Ausschnitt einen Fehler zu machen, ist es schon viel zu hackig.
Camilo Martin

9
Ich bin mit @CamiloMartin in Bezug auf die Anzahl der Zeilen im Vergleich zur Möglichkeit von Fehlern nicht einverstanden. Obwohl substr_replacees sich aufgrund aller Parameter um eine etwas unhandliche Funktion handelt, besteht das eigentliche Problem darin, dass die Manipulation von Zeichenfolgen durch Zahlen manchmal nur schwierig ist. Sie müssen darauf achten, die richtige Variable / den richtigen Offset an die Funktionen zu übergeben. Ich würde sogar so weit gehen zu sagen, dass der obige Code der einfachste und für mich logischste Ansatz ist.
Alex

1
Genialer Ansatz. Funktioniert perfekt, wenn Variablenwerte ersetzt werden, in denen Regex-Zeichen reserviert sind (preg_replace ist also bear). Das ist unkompliziert und elegant.
Praesagus

96

Bearbeiten: Beide Antworten wurden aktualisiert und sind jetzt korrekt. Ich werde die Antwort hinterlassen, da die Funktionszeiten immer noch nützlich sind.

Die Antworten von 'zombat' und 'too much php' sind leider nicht korrekt. Dies ist eine Überarbeitung der Antwort von zombat (da ich nicht genug Ruf habe, um einen Kommentar zu schreiben):

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

Beachten Sie die Strlen ($ Nadel) anstelle von Strlen ($ Ersetzen). Das Beispiel von Zombat funktioniert nur dann korrekt, wenn Nadel und Ersatz dieselbe Länge haben.

Hier ist die gleiche Funktionalität in einer Funktion mit der gleichen Signatur wie PHPs eigener str_replace:

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

Dies ist die überarbeitete Antwort von 'zu viel PHP':

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

Beachten Sie die 2 am Ende anstelle von 1. Oder im Funktionsformat:

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

Ich habe die beiden Funktionen zeitlich festgelegt und die erste ist doppelt so schnell, wenn keine Übereinstimmung gefunden wird. Sie haben die gleiche Geschwindigkeit, wenn eine Übereinstimmung gefunden wird.


Warum generifizieren Sie dies nicht wie folgt: str_replace_flexible (gemischte $ s, gemischte $ r, int $ Offset, int $ Limit), wobei die Funktion $ limit Vorkommen ab dem $ offset (n-ten) Match ersetzt.
Adam Friedman

Schade, dass dies nur für Groß- und Kleinschreibung gilt.
andreszs

4
@ Andrew stripos()zur Rettung :-)
Gras Double

76

Ich habe mich gefragt, welches das schnellste ist, also habe ich sie alle getestet.

Unten finden Sie:

  • Eine umfassende Liste aller Funktionen, die auf dieser Seite bereitgestellt wurden
  • Benchmark-Test für jeden Beitrag (durchschnittliche Ausführungszeit über 10.000 Läufe)
  • Links zu jeder Antwort (für den vollständigen Code)

Alle Funktionen wurden mit den gleichen Einstellungen getestet:

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

Funktionen, die nur das erste Auftreten einer Zeichenfolge innerhalb einer Zeichenfolge ersetzen :


Funktionen, die nur das letzte Vorkommen einer Zeichenfolge innerhalb einer Zeichenfolge ersetzen :


Vielen Dank dafür, ich verwende im Allgemeinen preg_replace, da es am flexibelsten ist, wenn zukünftige Optimierungen in den meisten Fällen erforderlich sind. 27% langsamer werden nicht signifikant sein
zzapper

@oLinkWebDevelopment Ich würde gerne Ihr Benchmark-Skript sehen. Ich denke, es könnte sich als nützlich erweisen.
Dave Morton

Der Grund, warum substr_replace()das Ergebnis gewonnen wird, ist einfach; weil es eine interne Funktion ist. Zwei interne und benutzerdefinierte Funktionen, die dasselbe tun, unterscheiden sich in der Leistung, da die interne Funktion in niedrigeren Ebenen ausgeführt wird. Warum also nicht preg_match()? Reguläre Ausdrücke sind fast langsamer als jede interne String-Manipulationsfunktion, da sie mehrmals in einem String suchen.
MAChitgarha

1
Ich hoffe, dass der Benchmark für Ihren "Gewinner" ( substr_replace($string, $replace, 0, strlen($search));) nicht nur diese Statik geschrieben hat 0. Ein Teil der Faltung von Nicht-Regex-Lösungen besteht darin, dass sie den Ausgangspunkt "finden" müssen, bevor sie wissen, wo sie ersetzt werden müssen.
Mickmackusa

55

Leider kenne ich keine PHP-Funktion, die dies kann.
Sie können Ihre eigenen ziemlich einfach so rollen:

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

Ich denke, dies ist die Golfversion von allen - mit joinstatt implode.
Titus

return implode($replace, explode($find, $subject, $limit+1));für benutzerdefinierte
Ersatznummern

7

Ich habe diese kleine Funktion erstellt, die String für String (Groß- und Kleinschreibung beachten) durch Limit ersetzt, ohne dass Regexp erforderlich ist. Es funktioniert gut.

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

Anwendungsbeispiel:

$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

Obwohl ich es lieber tun ===falsewürde is_bool(, als expliziter zu sein - ich gebe diesen Daumen auf, nur weil er den RegExp-Wahnsinn vermieden hat ! ... und gleichzeitig funktioniert es und saubere Lösung ...
jave.web

Eine leicht anpassbare preg_Lösung zu bevorzugen, ist kein Wahnsinn, sondern eine persönliche Präferenz. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);ist ziemlich einfach zu lesen für Leute, die keine Angst vor Regex haben. Benötigen Sie eine Suche ohne Berücksichtigung der Groß- und Kleinschreibung? Fügen Sie inach dem Ende Muster - Begrenzung. Benötigen Sie Unicode / Multibyte-Unterstützung? Fügen Sie unach dem Ende Muster - Begrenzung. Benötigen Sie Unterstützung für Wortgrenzen? Fügen Sie \bauf beiden Seiten Ihrer Suchzeichenfolge hinzu. Wenn Sie keinen regulären Ausdruck wünschen, verwenden Sie keinen regulären Ausdruck. Pferde für Kurse, aber sicher kein Wahnsinn.
Mickmackusa

3

Am einfachsten wäre es, reguläre Ausdrücke zu verwenden.

Die andere Möglichkeit besteht darin, die Position des Strings mit strpos () und dann mit substr_replace () zu ermitteln.

Aber ich würde mich wirklich für die RegExp entscheiden.


Dieser "Hinweis" ist im Vergleich zu anderen Beiträgen auf dieser Seite eher vage / von geringem Wert.
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));
}

Nur-Code-Antworten sind bei StackOverflow von geringem Wert, da sie Tausende zukünftiger Forscher schlecht ausbilden / befähigen.
Mickmackusa

3

=> CODE WURDE ÜBERARBEITET, daher sollten einige Kommentare zu alt sein

Und vielen Dank an alle, die mir geholfen haben, das zu verbessern

Irgendwelche BUG, ​​bitte teilen Sie mir mit; Ich werde das gleich danach beheben

Also los geht's für:

Ersetzen des ersten 'o' durch 'ea' zum Beispiel:

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

//output: I leave you

Die Funktion:

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

Schlägt fehl, wenn $ dies Zeichen wie aaa vs aaaaaaaaa
Cristo

Ich denke das sollte substr($where,$b+strlen($this))nicht sein substr($where,$b+1). Und ich denke das substr_replaceist schneller.
Titus

Code wurde überarbeitet, jetzt funktioniert es auch für lange Saiten
PYK

Diese Lösung funktioniert nicht wie codiert. Beweis: 3v4l.org/cMeZj Und wenn Sie das Problem mit der Benennung von Variablen beheben, funktioniert es nicht, wenn der Suchwert nicht gefunden wird - es beschädigt die Eingabezeichenfolge. Beweis: 3v4l.org/XHtfc
mickmackusa

Ist es fair für jemanden, der den Code FIXIEREN möchte? @mickmackusa Kannst du das bitte nochmal überprüfen?
PYK

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

Dies ist genau das gleiche wie die erste Antwort. Außerdem sollten Sie ein preg_quotevon ausführen, $findbevor Sie es als Ausdruck verwenden.
Emil Vikström

Das habe ich benutzt, also habe ich es hochgestimmt. Die erste Antwort verursachte einen Konflikt mit Drupal. Sie muss eine Drupal-Hilfsfunktion überschrieben haben. Also habe ich einfach den Code genommen, der sich in der Funktion befand, und ihn in Übereinstimmung mit dem Rest des Codes verwendet ...
Dan Mantyla

Diese Nur-Code-Antwort bietet redundante Ratschläge auf der Seite (ganz zu schweigen davon, dass sie fehlen preg_quote(). Diese späte doppelte Antwort kann sicher von der Seite gelöscht werden, da ihre Ratschläge von der früheren und höher bewerteten akzeptierten Antwort bereitgestellt werden.
mickmackusa

2

Um die Antwort von @ renocor zu erweitern , habe ich eine Funktion geschrieben, die zu 100% abwärtskompatibel ist str_replace(). Das heißt, Sie ersetzen können alle Vorkommen str_replace()mit , str_replace_limit()ohne etwas durcheinander, auch die Verwendung von Arrays für die $search, $replaceund / oder $subject.

Die Funktion könnte vollständig in sich geschlossen sein, wenn Sie den Funktionsaufruf durch ersetzen möchten ($string===strval(intval(strval($string)))), aber ich würde dagegen empfehlen, da dies valid_integer()eine ziemlich nützliche Funktion ist, wenn Sie Ganzzahlen verwenden, die als Zeichenfolgen bereitgestellt werden.

Hinweis: Wann immer möglich, str_replace_limit()wird str_replace()stattdessen verwendet, sodass alle Anrufe an str_replace()durch ersetzt werden können, str_replace_limit()ohne sich um einen Leistungseinbruch sorgen zu müssen.

Verwendungszweck

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

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

1 Ersatz - bbcabc

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

2 Ersetzungen - bbcbbc

Funktion

<?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
ein bisschen aufgebläht, wenn du mich fragst. Was ich an dieser Lösung am meisten "hasse", ist auch die Fehlerbehandlung. Es bricht das Skript, wenn Sie falsche Werte übergeben. Sie denken, es sieht professionell aus, aber es ist nicht so, dass Sie anstelle eines Fehlers stattdessen einen Hinweis oder eine Warnung erstellen. Besser ist es, den Bullshit zu überspringen, stattdessen false oder null zurückzugeben und in einer solchen Funktion niemals eine Rückverfolgung zu verwenden. Die beste Lösung ist, dass der Programmierer entscheiden kann, was zu tun ist, wenn die Ausgabe falsch / unerwartet ist.
Codebeat

@Erwinus Diese nutzt im E_USER_WARNINGgesamten Gebäude , das eine ist Warnung , nicht ein Fehler . Die Rückverfolgung ist äußerst nützlich, um herauszufinden, welcher Code die ungültigen Daten überhaupt an die Funktion weitergibt (was unbedingt erforderlich ist, um Fehler in der Produktion aufzuspüren). Wie bei der Rückkehr $subjectstatt false/ nulloder einen Fehler zu werfen, die einfach eine persönliche Entscheidung für meinen Anwendungsfall war. Um mit str_replace()der Funktionalität übereinzustimmen, ist es am besten, abfangbare schwerwiegende Fehler zu verwenden (wie dies auch der str_replace()Fall ist, wenn die ersten beiden Argumente geschlossen werden).
0b10011

Ah, ich habe nichts von der E_USER_WARNING bemerkt, die Sie verwenden. Tut mir leid. Das Problem bei der Rückgabe des Betreffs ist, dass Sie nie sehen können, dass außerhalb der Funktion etwas nicht stimmt. Das heißt, die Funktion kann halb so groß sein, wenn Sie es intelligenter machen (es ist möglich). Zweitens sind Kommentare in Ordnung, wenn sie etwas Komplexes erklären, aber für einfache Dinge wie die Wertsteigerung nicht sehr nützlich sind. Insgesamt halte ich es für unnötig groß. Die Verwendung von Warnungen in einer Produktionsumgebung kann auch ein Sicherheitsproblem darstellen, wenn Sie diesen Code auf einem Server verwenden, der Laufzeitnachrichten standardmäßig nicht unterdrückt (Protokolle).
Codebeat

@Erwinus Ich war ausführlich, wenn es um die Kommentare ging, weil einige Leute die Sprache nicht so gut verstehen wie andere, und Kommentare können immer von denen entfernt werden, die sie verstehen. Wenn Sie einen besseren Weg kennen, um für alle Randfälle das gleiche Endergebnis zu erzielen, bearbeiten Sie auf jeden Fall die Antwort. Und wenn Ihre Produktionsumgebung keine Fehlermeldungen unterdrückt, haben Sie ein größeres Problem als diese Funktion;)
0b10011

TL; DR Dieses Snippet ist so aufgebläht, dass ich mir nicht vorstellen kann, es einer Regex-Funktion vorzuziehen (ich hasse es zu scrollen). Wenn Sie die vorgenommenen Ersetzungen zählen möchten, gibt es dafür einen Parameter in preg_replace(). Darüber hinaus bietet preg_replace()/ regex die Behandlung von Wortgrenzen (falls gewünscht) - etwas, das Nicht-Regex-Funktionen nicht elegant bieten.
Mickmackusa

2

Nach meinem Testergebnis möchte ich den von karim79 bereitgestellten regulären_Ausdruck abstimmen. (Ich habe nicht genug Ruf, um jetzt darüber abzustimmen!)

Die Lösung von zombat verwendet zu viele Funktionsaufrufe, ich vereinfache sogar die Codes. Ich verwende PHP 5.4, um beide Lösungen 100.000 Mal auszuführen, und hier ist das Ergebnis:

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

==> 1,85 Sek

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

==> 1,35 Sek

Wie du siehst. Die Leistung von preg_replace ist nicht so schlecht, wie viele Leute denken. Daher würde ich die klassische Lösung vorschlagen, wenn Ihr regulärer Express nicht kompliziert ist.


Ihr erstes Snippet ist ein unfairer Vergleich, da es keine korrekte Implementierung verwendet. Sie suchen nicht $posnach false. Wenn die Nadel also nicht im Heuhaufen vorhanden ist, wird die Ausgabe beschädigt.
Mickmackusa

Danke @mickmackusa, du hast recht. Aber darum geht es nicht. Ich sagte, dieser Code sei vereinfacht, um die Effizienz von Implementierungen zu vergleichen.
Hunter Wu

Das ist genau mein Punkt. Sie dürfen niemals Benchmark-Vergleiche durchführen, die nicht genau denselben Prozess ausführen. Der Vergleich von Äpfeln mit Halborangen ist nicht sinnvoll. Durch die vollständige Implementierung des vollständigen Nicht-Regex-Ansatzes wird der Geschwindigkeitsunterschied noch größer.
Mickmackusa

Nochmals vielen Dank. Ich möchte jedoch die bessere Implementierung finden und keinen tiefgreifenden Unterschied machen.
Hunter Wu

2

Um die Antwort von zombat (die meiner Meinung nach die beste Antwort ist) zu erweitern, habe ich eine rekursive Version seiner Funktion erstellt, die einen $limitParameter enthält, der angibt, wie viele Vorkommen Sie ersetzen möchten.

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

Beachten Sie, dass keine Qualitätsprüfung aktiviert $start_posist. Wenn die Zeichenfolgenlänge überschritten wird, generiert diese Funktion : Warning: strpos(): Offset not contained in string.... Diese Funktion kann nicht ersetzt werden, wenn die $start_posLänge überschritten wird. Fehlernachweis: 3v4l.org/qGuVIR ... Ihre Funktion kann die return $haystackBedingungen kombinieren und vermeiden, Einwegvariablen wie diese zu deklarieren : 3v4l.org/Kdmqp Wie ich jedoch in den Kommentaren an anderer Stelle auf dieser Seite gesagt habe, würde ich es vorziehen Verwenden Sie einen sehr sauberen, direkten, nicht rekursiven preg_replace()Aufruf.
Mickmackusa

Ja, damit Sie diese Zeile elsehinzufügen können$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A

2

Für eine Zeichenfolge

$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

Für ein einzelnes Zeichen

$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

Das erste substr_replace () - Snippet schlägt fehl, wenn sich die Suchzeichenfolge nicht am Offset 0 der Eingabezeichenfolge befindet. Fehlernachweis : 3v4l.org/oIbRv Beide substr_replace()Techniken beschädigen die Eingabezeichenfolge, wenn der Suchwert nicht vorhanden ist. Beweis des Scheiterns: 3v4l.org/HmEml (Und diese letzte Technik mit allen revAnrufen ist ernsthaft verworren / hart für die Augen.)
mickmackusa

2

Denken Sie daran, dass die gesamte Zeichenfolge ein Array ist:

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

$string[0] = "B";

echo $string;

"Borem ipsum lá lá lá"


3
Es sei denn, es enthält Multibyte-Zeichen ... und dann schlägt Ihre Technik fehl. Wie bedauerlich, dass Sie eine Beispiel-Eingabezeichenfolge mit angeboten haben á. Demonstration des Scheiterns
mickmackusa

Sie können überprüfen, ob stringes sich um eine Multibyte-Zeichenfolge handelt, indem Siemb_strlen($subject) != strlen($subject)
RousseauAlexandre

Dieser Beitrag versucht nicht, die gestellte Frage zu beantworten.
Mickmackusa

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

Mit substr_replace können wir das Auftreten des ersten Zeichens nur in einer Zeichenfolge ersetzen. wie & wird mehrmals wiederholt, aber nur an der ersten Position müssen wir & durch ersetzen?


1

Diese Funktion ist stark von der Antwort von @renocor inspiriert. Es macht die Funktion Multi-Byte-sicher.

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

Sie können dies verwenden:

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

Fand dieses Beispiel von php.net

Verwendungszweck:

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

Ausgabe:

ThiZ iz an examplz

Dies kann die Leistung ein wenig verringern, ist aber die einfachste Lösung.


Wenn das die Ausgabe ist, worum geht es dann? Sollte es nicht nur das erste Kleinbuchstaben "z" durch ein Großbuchstaben "Z" ersetzen? Anstatt alle zu ersetzen? Ich dachte, das war es, worüber wir hier sprachen ...
Swivel

Mein schlechtes, es wird nur das erste Vorkommen ersetzen. Bearbeitet.
happyhardik

Der gleiche Rat wurde bereits fast drei Jahre zuvor von Bas angeboten (und ohne übermäßig anzurufen strpos()). Heruntergestuft, da der Seite kein neuer Wert hinzugefügt wird.
Mickmackusa

0

Wenn Ihre Zeichenfolge keine Multibyte-Zeichen enthält und Sie nur ein Zeichen ersetzen möchten, können Sie sie einfach verwenden strpos

Hier eine Funktion, die Fehler behandelt

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

Für Schleifenlösung

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

Hier ist eine einfache Klasse, die ich erstellt habe, um unsere leicht modifizierten Funktionen str_replace () zu verpacken .

Mit unserer Funktion php :: str_rreplace () können Sie auch einen umgekehrten, begrenzten str_replace () ausführen, was sehr praktisch sein kann, wenn Sie versuchen, nur die letzten X-Instanzen eines Strings zu ersetzen.

Diese Beispiele verwenden beide 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) );
    }
}

Ihr Beitrag bietet keinen Mehrwert für diese bereits überfüllte Seite. Ihre Regex-Lösung schlägt in vielen Randfällen fehl, weil Sie das falsche Werkzeug verwendet haben, um Zeichen in der Nadelkette zu maskieren. Fehlernachweis : 3v4l.org/dTdYK Die stark positiv bewertete und akzeptierte Antwort aus dem Jahr 2009 zeigt bereits die ordnungsgemäße Ausführung dieser Technik. Ihre zweite Methode beantwortet die gestellte Frage nicht und wurde bereits von oLinkWebDevelopment bereitgestellt.
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")

Es gibt noch einen zusätzlichen Speicherplatz, aber es spielte keine Rolle, da es sich in meinem Fall um ein Backgound-Skript handelte.


Diese Technik wurde 2009 von toomuchphp zur Verfügung gestellt ! Ich habe abgelehnt, weil Ihr Beitrag den Forschern keinen neuen Wert verleiht. Stellen Sie vor dem Veröffentlichen einer Antwort sicher, dass Ihre Lösung für die Seite eindeutig ist und der Seite einen Mehrwert verleiht.
Mickmackusa

-3

Dies ist meine erste Antwort hier, ich hoffe es richtig zu machen. Warum nicht das vierte Argument der Funktion str_replace für dieses Problem verwenden?

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

count: Wenn bestanden, wird dies auf die Anzahl der durchgeführten Ersetzungen gesetzt.


edit: Diese Antwort ist falsch, da der 4. Parameter von str_replace eine Variable ist, der die Anzahl der durchgeführten Ersetzungen zugewiesen wird. Dies ist nicht konsistent mit preg_replace , das einen 4. Parameter $limitund einen 5. Parameter hat &$count.


Die vierten Argumente werden von str_replace () auf die Anzahl der vorgenommenen Ersetzungen gesetzt. Aus diesem Grund erhalten Sie eine Fehlermeldung, wenn Sie eine Ganzzahl und keine Variable übergeben.
Arminrosu

-6

Es ist einfach, eine Lösung zu finden, um nur die ersten oder ersten Instanzen zu ersetzen (indem Sie den Zählwert angeben). Es gibt nicht viele Lösungen, um die letzten oder letzten Instanzen zu ersetzen.

Vielleicht sollte so etwas wie str_replace ($ find, $ replace, $ subject, -3) die letzten drei Instanzen ersetzen.

Jedenfalls nur ein Vorschlag.


4
Warum eine Frage mit einem Vorschlag beantworten, wenn eine Antwort zwei Jahre zuvor angenommen wurde?!
mbinette

Auch -3 funktioniert nicht als Parameter, da der 4. Parameter ausgegeben und kein Eingabeparameter ist. Es wäre besser, wenn Sie testen, was Sie vorschlagen, anstatt einen Code zu veröffentlichen, der abstürzt.
Ghostwriter78

Ja, das ist falsch, aber warum bekomme ich einen Absturz auf einem leeren Bildschirm, wenn ich es versuche? Ich habe die übliche Fehlerberichterstattung (E_ALL) durchgeführt. ini_set ("display_errors", 1); Immer noch leerer Bildschirmfehler.
Doug Cassidy
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.