Wie ersetze ich alle Vorkommen eines Zeichens in einer Zeichenkette?

480

Was ist der effektive Weg, um alle Vorkommen eines Charakters durch einen anderen Charakter in zu ersetzen std::string?

big-z
quelle

Antworten:

742

std::stringenthält keine solche Funktion, aber Sie können die eigenständige replaceFunktion aus dem algorithmHeader verwenden.

#include <algorithm>
#include <string>

void some_func() {
  std::string s = "example string";
  std::replace( s.begin(), s.end(), 'x', 'y'); // replace all 'x' to 'y'
}
Kirill V. Lyadvinsky
quelle
6
std::stringist ein Container, der speziell für die Arbeit mit Zeichenfolgen entwickelt wurde. Link
Kirill V. Lyadvinsky
164
Leider kann dadurch nur ein Zeichen durch ein anderes Zeichen ersetzt werden. Es kann ein Zeichen nicht durch mehr Zeichen ersetzen (dh durch eine Zeichenfolge). Gibt es eine Möglichkeit, eine Suche durch mehr Zeichen zu ersetzen?
SasQ
6
@Kirill V. Lyadvinsky Was ist, wenn ich nur ein Ereignis entfernen möchte.
SIFE
4
@ KirillV.Lyadvinsky: Wenn ich diese Methode verwende, um alle x durch y zu ersetzen, ist das Ergebnis eine lange y-Zeichenfolge, unabhängig von der ursprünglichen Zeichenfolge. Ich bin gespannt, was Ihrer Meinung nach das Problem sein würde. (Der Code ist genau der gleiche wie Sie geschrieben haben)
Transzendent
6
@Transcendent: Genau das passiert mit std::string::replace()statt std::replace()! 'x' ( char) wird implizit in size_t[Wert 120] umgewandelt, sodass die gesamte Zeichenfolge oder ein Teil davon mit 120 Kopien von 'y' gefüllt wird.
IBue
127

Ich dachte, ich würde auch die Boost-Lösung einwerfen:

#include <boost/algorithm/string/replace.hpp>

// in place
std::string in_place = "blah#blah";
boost::replace_all(in_place, "#", "@");

// copy
const std::string input = "blah#blah";
std::string output = boost::replace_all_copy(input, "#", "@");
OnkelZeiv
quelle
Dann fehlen Ihnen einige -IFlags für Ihren Compiler, damit er die Boost-Bibliotheken auf Ihrem System findet. Vielleicht müssen Sie es zuerst installieren.
Martin Ueding
Das obige ist effektiver, da es mit std lib herauskommt. Nicht alle, die die Boost-Bibliothek verwenden ;-)
hfrmobile
122

Die Frage konzentriert sich auf das characterErsetzen, aber da ich diese Seite sehr nützlich fand (insbesondere Konrads Bemerkung), möchte ich diese allgemeinere Implementierung teilen, die auch Folgendes ermöglicht substrings:

std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
    }
    return str;
}

Verwendungszweck:

std::cout << ReplaceAll(string("Number Of Beans"), std::string(" "), std::string("_")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("X")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("h")) << std::endl;

Ausgänge:

Number_Of_Beans

XXjXugtXty

hhjhugthty


BEARBEITEN:

Das Obige kann in geeigneterer Weise implementiert werden, falls die Leistung von Belang ist, indem nichts zurückgegeben wird ( void) und die Änderungen direkt an der strals Argument angegebenen Zeichenfolge ausgeführt werden, die als Adresse statt als Wert übergeben wird . Dies würde eine nutzlose und kostspielige Kopie der Originalzeichenfolge vermeiden und gleichzeitig das Ergebnis zurückgeben. Ihr Anruf dann ...

Code:

static inline void ReplaceAll2(std::string &str, const std::string& from, const std::string& to)
{
    // Same inner code...
    // No return statement
}

Hoffe, dass dies für einige andere hilfreich sein wird ...

Gauthier Boaglio
quelle
4
Dieser hat ein Leistungsproblem in Fällen, in denen die Quellzeichenfolge groß ist und die Zeichenfolge häufig ersetzt werden muss. string :: replace () wird oft aufgerufen, was viele String-Kopien verursacht. Siehe meine Lösung, die dieses Problem behebt.
Minastaros
1
Nit Picking voraus: nach Adresse => nach Referenz . Ob es sich um eine Adresse handelt oder nicht, ist ein Implementierungsdetail.
Max Truxa
1
Sie sollten tatsächlich prüfen, ob die fromZeichenfolge leer ist, da sonst eine Endlosschleife auftritt.
Neuling
34

Stellen Sie sich einen großen binären Blob vor, bei dem alle 0x00 Bytes durch "\ 1 \ x30" und alle 0x01 Bytes durch "\ 1 \ x31" ersetzt werden sollen, da das Transportprotokoll keine \ 0 Bytes zulässt.

In Fällen, in denen:

  • Die ersetzende und die zu ersetzende Zeichenfolge haben unterschiedliche Längen.
  • Es gibt viele Vorkommen der zu ersetzenden Zeichenfolge innerhalb der Quellzeichenfolge und
  • die Quellzeichenfolge ist groß,

Die bereitgestellten Lösungen können nicht angewendet werden (da sie nur einzelne Zeichen ersetzen) oder haben ein Leistungsproblem, da sie string :: replace mehrmals aufrufen würden, wodurch immer wieder Kopien der Größe des Blobs generiert werden. (Ich kenne die Boost-Lösung nicht, vielleicht ist es aus dieser Perspektive in Ordnung)

Dieser geht alle Vorkommen in der Quellzeichenfolge entlang und erstellt die neue Zeichenfolge Stück für Stück einmal :

void replaceAll(std::string& source, const std::string& from, const std::string& to)
{
    std::string newString;
    newString.reserve(source.length());  // avoids a few memory allocations

    std::string::size_type lastPos = 0;
    std::string::size_type findPos;

    while(std::string::npos != (findPos = source.find(from, lastPos)))
    {
        newString.append(source, lastPos, findPos - lastPos);
        newString += to;
        lastPos = findPos + from.length();
    }

    // Care for the rest after last occurrence
    newString += source.substr(lastPos);

    source.swap(newString);
}
Minastaros
quelle
Dies ist bei weitem die beste Lösung, die allein auf der STL basiert. Wenn Sie eine benutzerdefinierte Funktion zur einfachen Verwendung überall hinzufügen möchten, machen Sie diese.
Roger Sanders
21

Ein einfaches Suchen und Ersetzen eines einzelnen Zeichens würde ungefähr so ​​aussehen:

s.replace(s.find("x"), 1, "y")

Um dies für die gesamte Zeichenfolge zu tun, ist es einfach, eine Schleife zu erstellen, bis Sie s.findzurückkehren npos. Ich nehme an, Sie könnten auch fangen range_error, um die Schleife zu verlassen, aber das ist irgendwie hässlich.

TED
quelle
7
Dies ist wahrscheinlich eine geeignete Lösung, wenn die Anzahl der zu ersetzenden Zeichen im Vergleich zur Länge der Zeichenfolge gering ist, sie lässt sich jedoch nicht gut skalieren. Wenn der Anteil der Zeichen in der ursprünglichen Zeichenfolge, die ersetzt werden müssen, zunimmt, nähert sich diese Methode mit der Zeit O (N ^ 2).
undund
7
Wahr. Meine allgemeine Philosophie ist es, das Einfache (Schreiben und Lesen) zu tun, bis die Ineffizienzen echte Probleme verursachen. Es gibt einige Umstände, unter denen Sie möglicherweise humorvolle Saiten haben, bei denen O (N ** 2) wichtig ist, aber in 99% der Fälle sind meine Saiten 1K oder weniger.
Ted
3
... Davon abgesehen mag ich Kirills Methode besser (und hatte sie bereits abgestimmt).
Ted
Was passiert, wenn "x" nicht gefunden wird? Warum verwenden Sie auch doppelte Zahnspangen?
Prasath Govind
@PrasathGovind - Ich habe nur die erforderlichen Anrufe angezeigt (daher "so etwas wie"). Wichtige, aber undurchsichtige Details wie die richtige Fehlerbehandlung wurden dem Leser als Übung überlassen. Was "Doppelklammern" betrifft, bin ich mir nicht sicher, was das ist oder wovon Sie sprechen. Für mich ist eine "Klammer" der {Charakter. Ich weiß nicht, was eine "Doppelklammer" ist. Vielleicht haben Sie ein Schriftproblem?
TED
6

Wenn Sie mehr als ein einzelnes Zeichen std::stringersetzen möchten und nur damit arbeiten, funktioniert dieses Snippet und ersetzt sNeedle in sHaystack durch sReplace, und sNeedle und sReplace müssen nicht dieselbe Größe haben. Diese Routine verwendet die while-Schleife, um alle Vorkommen zu ersetzen, und nicht nur die erste, die von links nach rechts gefunden wurde.

while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}
Volomike
quelle
Dies ist O (n ^). Sie könnten es in O (n) Zeit tun.
Changming Sun
3
@ChangmingSun welche O (n) Lösung meinst du?
Habakuk
2
Dies führt zu einer Endlosschleife, wenn kNeedle ein Teilstring von sReplace ist.
Stolz
Außerdem gibt es findzweimal einen Anruf. Erwägen Sie, dieses Ergebnis zu einer temporären Variablen zu machen.
Luc Bloom
4

Verwenden Sie, wie Kirill vorgeschlagen hat, entweder die Ersetzungsmethode oder iterieren Sie entlang der Zeichenfolge, um jedes Zeichen unabhängig voneinander zu ersetzen.

Alternativ können Sie die findMethode verwenden oder find_first_ofje nachdem, was Sie tun müssen. Keine dieser Lösungen erledigt die Aufgabe auf einmal, aber mit ein paar zusätzlichen Codezeilen sollten Sie sie für Sie arbeiten lassen. :-)

Konrad
quelle
3
#include <iostream>
#include <string>
using namespace std;
// Replace function..
string replace(string word, string target, string replacement){
    int len, loop=0;
    string nword="", let;
    len=word.length();
    len--;
    while(loop<=len){
        let=word.substr(loop, 1);
        if(let==target){
            nword=nword+replacement;
        }else{
            nword=nword+let;
        }
        loop++;
    }
    return nword;

}
//Main..
int main() {
  string word;
  cout<<"Enter Word: ";
  cin>>word;
  cout<<replace(word, "x", "y")<<endl;
  return 0;
}
Lloydie
quelle
Wenn wordes lang ist, kann es beim Aufrufen der Funktion zu einem hohen Overhead kommen. Sie können diese optimieren , indem man word, targetund replacementals const Verweise.
TrebledJ
2

Was ist mit Abseil StrReplaceAll ? Aus der Header-Datei:

// This file defines `absl::StrReplaceAll()`, a general-purpose string
// replacement function designed for large, arbitrary text substitutions,
// especially on strings which you are receiving from some other system for
// further processing (e.g. processing regular expressions, escaping HTML
// entities, etc.). `StrReplaceAll` is designed to be efficient even when only
// one substitution is being performed, or when substitution is rare.
//
// If the string being modified is known at compile-time, and the substitutions
// vary, `absl::Substitute()` may be a better choice.
//
// Example:
//
// std::string html_escaped = absl::StrReplaceAll(user_input, {
//                                                {"&", "&amp;"},
//                                                {"<", "&lt;"},
//                                                {">", "&gt;"},
//                                                {"\"", "&quot;"},
//                                                {"'", "&#39;"}});
hotblack944
quelle
1

Alte Schule :-)

std::string str = "H:/recursos/audio/youtube/libre/falta/"; 

for (int i = 0; i < str.size(); i++) {
    if (str[i] == '/') {
        str[i] = '\\';
    }
}

std::cout << str;

Ergebnis:

H: \ recursos \ audio \ youtube \ libre \ falta \

Iván Rodríguez
quelle
0

Das funktioniert! Ich habe etwas Ähnliches für eine Buchhandlungs-App verwendet, in der das Inventar in einer CSV (wie einer .dat-Datei) gespeichert wurde. Im Fall eines einzelnen Zeichens, dh der Ersetzer ist nur ein einzelnes Zeichen, z. B. '|', muss es in doppelten Anführungszeichen "|" stehen. um keine ungültige Konvertierung zu werfen const char.

#include <iostream>
#include <string>

using namespace std;

int main()
{
    int count = 0;  // for the number of occurences.
    // final hold variable of corrected word up to the npos=j
    string holdWord = "";
    // a temp var in order to replace 0 to new npos
    string holdTemp = "";
    // a csv for a an entry in a book store
    string holdLetter = "Big Java 7th Ed,Horstman,978-1118431115,99.85";

    // j = npos
    for (int j = 0; j < holdLetter.length(); j++) {

        if (holdLetter[j] == ',') {

            if ( count == 0 ) 
            {           
                holdWord = holdLetter.replace(j, 1, " | ");      
            }
            else {

                string holdTemp1 = holdLetter.replace(j, 1, " | ");

                // since replacement is three positions in length,
                // must replace new replacement's 0 to npos-3, with
                // the 0 to npos - 3 of the old replacement 
                holdTemp = holdTemp1.replace(0, j-3, holdWord, 0, j-3); 

                holdWord = "";

                holdWord = holdTemp;

            }
            holdTemp = "";
            count++;
        }
    } 
    cout << holdWord << endl;
    return 0;
}

// result:
Big Java 7th Ed | Horstman | 978-1118431115 | 99.85

Normalerweise verwende ich derzeit CentOS, daher ist meine Compilerversion unten aufgeführt. Die C ++ - Version (g ++), C ++ 98 Standard:

g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
oOpSgEo
quelle
0

Wenn Sie bereit sind, std::strings zu verwenden , können Sie die strsubFunktion dieser Beispiel-App unverändert verwenden oder aktualisieren, wenn Sie möchten, dass ein anderer Typ oder ein anderer Parametersatz verwendet wird, um ungefähr dasselbe Ziel zu erreichen. Grundsätzlich werden die Eigenschaften und Funktionen von verwendet std::string, um den übereinstimmenden Zeichensatz schnell zu löschen und die gewünschten Zeichen direkt in das Zeichen einzufügen std::string. Jedes Mal, wenn dieser Ersetzungsvorgang ausgeführt wird, wird der Offset aktualisiert, wenn immer noch passende Zeichen zum Ersetzen gefunden werden können. Wenn er nicht ersetzt werden kann, gibt er die Zeichenfolge in ihrem Zustand vom letzten Update zurück.

#include <iostream>
#include <string>

std::string strsub(std::string stringToModify,
                   std::string charsToReplace,
                   std::string replacementChars);

int main()
{
    std::string silly_typos = "annoiiyyyng syyyllii tiipos.";

    std::cout << "Look at these " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos, "yyy", "i");
    std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos, "ii", "y");

    std::cout << "There, no more " << silly_typos << std::endl;
    return 0;
}

std::string strsub(std::string stringToModify,
                   std::string charsToReplace,
                   std::string replacementChars)
{
    std::string this_string = stringToModify;

    std::size_t this_occurrence = this_string.find(charsToReplace);
    while (this_occurrence != std::string::npos)
    {
        this_string.erase(this_occurrence, charsToReplace.size());
        this_string.insert(this_occurrence, replacementChars);
        this_occurrence = this_string.find(charsToReplace,
                                           this_occurrence + replacementChars.size());
    }

    return this_string;
}

Wenn Sie sich nicht auf die Verwendung von std::strings als Parameter verlassen möchten, um stattdessen Zeichenfolgen im C-Stil zu übergeben, sehen Sie das aktualisierte Beispiel unten:

#include <iostream>
#include <string>

std::string strsub(const char * stringToModify,
                   const char * charsToReplace,
                   const char * replacementChars,
                   uint64_t sizeOfCharsToReplace,
                   uint64_t sizeOfReplacementChars);

int main()
{
    std::string silly_typos = "annoiiyyyng syyyllii tiipos.";

    std::cout << "Look at these " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos.c_str(), "yyy", "i", 3, 1);
    std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
    silly_typos = strsub(silly_typos.c_str(), "ii", "y", 2, 1);

    std::cout << "There, no more " << silly_typos << std::endl;
    return 0;
}

std::string strsub(const char * stringToModify,
                   const char * charsToReplace,
                   const char * replacementChars,
                   uint64_t sizeOfCharsToReplace,
                   uint64_t sizeOfReplacementChars)
{
    std::string this_string = stringToModify;

    std::size_t this_occurrence = this_string.find(charsToReplace);
    while (this_occurrence != std::string::npos)
    {
        this_string.erase(this_occurrence, sizeOfCharsToReplace);
        this_string.insert(this_occurrence, replacementChars);
        this_occurrence = this_string.find(charsToReplace,
            this_occurrence + sizeOfReplacementChars);
    }

    return this_string;
}
kayleeFrye_onDeck
quelle
0

In einfachen Situationen funktioniert dies ziemlich gut, ohne eine andere Bibliothek als std :: string (die bereits verwendet wird) zu verwenden.

Ersetzen Sie alle Vorkommen von Zeichen a durch Zeichen b in some_string :

for (size_t i = 0; i < some_string.size(); ++i) {
    if (some_string[i] == 'a') {
        some_string.replace(i, 1, "b");
    }
}

Wenn die Zeichenfolge groß ist oder mehrere zu ersetzende Aufrufe ein Problem darstellen, können Sie die in dieser Antwort erwähnte Technik anwenden: https://stackoverflow.com/a/29752943/3622300

Guney Ozsan
quelle
0

Hier ist eine Lösung, die ich in einem maximalen DRI-Geist entwickelt habe. es sucht sNeedle in sHaystack und ersetzt es durch sReplace, nTimes wenn nicht 0, sonst alle sNeedle-Vorkommen. Der ersetzte Text wird nicht erneut gesucht.

std::string str_replace(
    std::string sHaystack, std::string sNeedle, std::string sReplace, 
    size_t nTimes=0)
{
    size_t found = 0, pos = 0, c = 0;
    size_t len = sNeedle.size();
    size_t replen = sReplace.size();
    std::string input(sHaystack);

    do {
        found = input.find(sNeedle, pos);
        if (found == std::string::npos) {
            break;
        }
        input.replace(found, len, sReplace);
        pos = found + replen;
        ++c;
    } while(!nTimes || c < nTimes);

    return input;
}
nicht bekannt gegeben
quelle