Ersetzen Sie einen Teil einer Zeichenfolge durch eine andere Zeichenfolge


185

Ist es in C ++ möglich, einen Teil eines Strings durch einen anderen zu ersetzen?

Grundsätzlich möchte ich dies tun:

QString string("hello $name");
string.replace("$name", "Somename");

Ich möchte aber die Standard C ++ - Bibliotheken verwenden.


1
Mögliches Duplikat von Was ist die Funktion, um einen String in C zu ersetzen? - Ups, tut mir leid, das ist C, nicht C ++; Ich wünschte, ich könnte abstimmen.
Polygenelubricants

1
@poly Ich würde denken, dass dies auch für C ++ gefragt worden sein muss, aber ich kann es nicht finden
Michael Mrozek

1
Es gibt ein Standard-Tag zu dieser Frage, aber vielleicht interessieren Sie sich auch für die String-Algorithmen von boost, die auch eine große Auswahl an Ersetzungsalgorithmen enthalten (Inplace / Copy, Groß- / Kleinschreibung beachten / Groß- und Kleinschreibung nicht beachten, First / Last / All / n-th ersetzen ).
OnkelBens

@Michael Mrozek Es gibt eine unter stackoverflow.com/questions/3418231/…, aber sie ist neuer und Ihre replaceAll-Methode hier ist robuster.
Dave-Holm

Antworten:


287

Es gibt eine Funktion zum Suchen eines Teilstrings in einem String ( find) und eine Funktion zum Ersetzen eines bestimmten Bereichs in einem String durch einen anderen String ( replace), sodass Sie diese kombinieren können, um den gewünschten Effekt zu erzielen:

bool replace(std::string& str, const std::string& from, const std::string& to) {
    size_t start_pos = str.find(from);
    if(start_pos == std::string::npos)
        return false;
    str.replace(start_pos, from.length(), to);
    return true;
}

std::string string("hello $name");
replace(string, "$name", "Somename");

Als Antwort auf einen Kommentar replaceAllwürde ich wahrscheinlich ungefähr so ​​aussehen:

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if(from.empty())
        return;
    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(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
    }
}

2
Wie würde ich das beheben, wenn die ursprüngliche Zeichenfolge mehr als eine Instanz von "$ name" hätte und ich alle ersetzen wollte?
Tom Leese

1
Warum nicht fromund topro weitergegeben constReferenz? Was funktioniert, wenn fromes nicht da ist? -1von mir dafür.
sbi

10
@sbi Behoben, obwohl Sie es als Empfehlungen anstelle von Angriffen hätten formulieren können - es ist mir einfach nicht in den Sinn gekommen, ich denke selten daran, es zu verwenden, constund wenn ich eine solche Dienstprogrammmethode schreiben würde, würde ich sie nur aufrufen, wenn ich das wüsste Ersatz waren gültig
Michael Mrozek

10
@ Michael: Gut, ich habe meine Abwertung in eine Aufwärtsabstimmung verwandelt. Das Ablehnen constignoriert eines der besten Tools von C ++. Das Übergeben pro constReferenz sollte der Standardmodus für Funktionsparameter sein. (FTR, ohne das constkönnten Sie nicht einmal String-Literale an Ihre Funktion übergeben, da Sie Temporäre nicht an Nicht- constReferenzen binden können . Die Funktion würde also nicht einmal das tun, wofür sie geschrieben wurde.)
sbi

19
Ist dies noch die einzige Lösung im Jahr 2018? Wenn dies der Fall ist und ein C ++ - Komitee dies liest, klären Sie es. Es ist peinlich. Bitte teilen (String, String) und ersetzen (String, String)!
user997112

95

Mit C ++ 11 können Sie std::regexwie folgt verwenden:

#include <regex>
...
std::string string("hello $name");
string = std::regex_replace(string, std::regex("\\$name"), "Somename");

Der doppelte Backslash ist erforderlich, um einem Escape-Zeichen zu entkommen.


Ich bin mir ziemlich sicher std::regex_replace, dass Qts String nicht akzeptiert wird.
BartoszKP

1
Du hast recht. Wie es passiert, bietet QString eine Ersetzungsmethode, die ein QRexExp akzeptiert und es ermöglicht, Qts eigenes Material zu verwenden. Aber ich denke , die aktuelle Antwort durch Ersetzen korrigiert werden kann stringmit string.toStdString().
Tom

1
Oder einfach durch Wechseln Stringzu std::string, weil die Frage nicht mit Qt zusammenhängt. Bitte denken Sie darüber nach - ich werde Ihre Antwort danach gerne positiv bewerten.
BartoszKP

5
Raw String ermöglicht das Schreiben R"(\$name)"anstelle von "\\$name".
Jarod42

2
Wie viel langsamer ist dies als Suchen / Ersetzen ohne Berücksichtigung der Konstruktionszeit von std :: regex?
jw_

18

std::stringhat eine replaceMethode, ist es das, wonach Sie suchen?

Du könntest es versuchen:

s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");

Ich habe es nicht selbst versucht, lese einfach die Dokumentation auf find()und replace().


2
Soweit ich sehen kann, akzeptiert die Methode std :: string replace nicht zwei Zeichenfolgen, wie ich möchte.
Tom Leese

2
Das funktioniert bei mir nicht. sizeof sollte durch string ("Somename") ersetzt werden. size () - 1
TimZaman

@ TimZaman: Das verwirrt mich, die Dokumentation besagt eindeutig, dass Sie aus einer Zeichenfolge im C-Stil initialisieren können.
SC Madsen

5
Das zweite Argument sollte die Länge von "$ name" sein (anstelle der Länge von "Somename"), nicht wahr?
Daniel Kiss

10

Verwenden Sie Folgendes, um die neue Zeichenfolge zurückzugeben:

std::string ReplaceString(std::string subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
    return subject;
}

Wenn Sie Leistung benötigen, finden Sie hier eine optimierte Funktion, mit der die Eingabezeichenfolge geändert wird. Es wird keine Kopie der Zeichenfolge erstellt:

void ReplaceStringInPlace(std::string& subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while ((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
}

Tests:

std::string input = "abc abc def";
std::cout << "Input string: " << input << std::endl;

std::cout << "ReplaceString() return value: " 
          << ReplaceString(input, "bc", "!!") << std::endl;
std::cout << "ReplaceString() input string not modified: " 
          << input << std::endl;

ReplaceStringInPlace(input, "bc", "??");
std::cout << "ReplaceStringInPlace() input string modified: " 
          << input << std::endl;

Ausgabe:

Input string: abc abc def
ReplaceString() return value: a!! a!! def
ReplaceString() input string not modified: abc abc def
ReplaceStringInPlace() input string modified: a?? a?? def

Ihr Aufruf von subject.replace in ReplaceStringInPlace () ändert das wirklich die Zeichenfolge inplace?
Damian

Ich habe kurz auf die Quelle geschaut und es sieht so aus, als würde die Bewegungssemantik verwendet, um die Vorderseite der alten Zeichenfolge an ihren Platz zu verschieben, sodass diese nicht kopiert wird, sondern das neu eingefügte Stück in die alte Zeichenfolge und den Schwanz der alten Zeichenfolge kopiert wird wird in die Größe des Puffers der alten Zeichenfolge kopiert. Es ist möglich, dass die Zeichenfolge so stark erweitert wird, dass der gesamte zugrunde liegende Puffer neu zugewiesen wird. Wenn Sie jedoch 1 zu 1 wie in seinem Beispiel ersetzen, geschieht dies meiner Meinung nach "an Ort und Stelle" oder ohne Kopieren. Wenn Sie die Zeichenfolge jedoch erweitern, Nur der erste Teil der alten Zeichenfolge wird nicht kopiert, und vielleicht erst dann.
Motes

6

Ja, Sie können dies tun, aber Sie müssen die Position der ersten Zeichenfolge mit dem Element find () der Zeichenfolge ermitteln und dann durch das Element replace () ersetzen.

string s("hello $name");
size_type pos = s.find( "$name" );
if ( pos != string::npos ) {
   s.replace( pos, 5, "somename" );   // 5 = length( $name )
}

Wenn Sie die Standardbibliothek verwenden möchten, sollten Sie sich unbedingt eine Kopie des Buches besorgen The C ++ Standard Library das all diese Dinge sehr gut abdeckt.


1
es ist size_t und nicht size_type
revo

1
Es ist std :: string :: size_type, nicht size_t oder der schmucklose size_type.
jmucchiello

5

Ich benutze im Allgemeinen Folgendes:

std::string& replace(std::string& s, const std::string& from, const std::string& to)
{
    if(!from.empty())
        for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size())
            s.replace(pos, from.size(), to);
    return s;
}

Es wird wiederholt aufgerufen std::string::find(), um andere Vorkommen der gesuchten Zeichenfolge zu lokalisieren, bis std::string::find()nichts mehr gefunden wird. Da std::string::find()die Position der Übereinstimmung zurückgegeben wird, besteht kein Problem darin, Iteratoren ungültig zu machen.


4

Das klingt nach einer Option

string.replace(string.find("%s"), string("%s").size(), "Something");

Sie könnten dies in eine Funktion einschließen, aber diese einzeilige Lösung klingt akzeptabel. Das Problem ist, dass dies nur das erste Vorkommen ändert. Möglicherweise möchten Sie eine Schleife darüber ausführen, aber Sie können auch mehrere Variablen mit demselben Token in diese Zeichenfolge einfügen ( %s)


1
Wie der Stil, fand aber die verschiedenen Saiten verwirrend ^^str.replace(str.find("%s"), string("%s").size(), "Something");
Paul Würtz

3

Wenn alle Zeichenfolgen std :: string sind, treten bei der Verwendung merkwürdige Probleme mit dem Abschneiden von Zeichen auf, sizeof()da diese für C-Zeichenfolgen und nicht für C ++ - Zeichenfolgen bestimmt sind. Das Update besteht darin, die .size()Klassenmethode von zu verwenden std::string.

sHaystack.replace(sHaystack.find(sNeedle), sNeedle.size(), sReplace);

Das ersetzt sHaystack inline - es ist nicht erforderlich, eine = Zuweisung vorzunehmen.

Anwendungsbeispiel:

std::string sHaystack = "This is %XXX% test.";
std::string sNeedle = "%XXX%";
std::string sReplace = "my special";
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
std::cout << sHaystack << std::endl;

2
wstring myString = L"Hello $$ this is an example. By $$.";
wstring search = L"$$";
wstring replace = L"Tom";
for (int i = myString.find(search); i >= 0; i = myString.find(search))
    myString.replace(i, search.size(), replace);

2

Wenn Sie es schnell erledigen möchten, können Sie einen Zwei-Scan-Ansatz verwenden. Pseudocode:

  1. erste Analyse. Finde heraus, wie viele passende Zeichen es gibt.
  2. Erweitern Sie die Länge der Zeichenfolge.
  3. zweite Analyse. Beginnen Sie am Ende der Zeichenfolge, wenn wir eine Übereinstimmung erhalten, die wir ersetzen. Andernfalls kopieren wir einfach die Zeichen aus der ersten Zeichenfolge.

Ich bin mir nicht sicher, ob dies zu einem In-Place-Algo optimiert werden kann.

Und ein C ++ 11-Codebeispiel, aber ich suche nur nach einem Zeichen.

#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

void ReplaceString(string& subject, char search, const string& replace)
{   
    size_t initSize = subject.size();
    int count = 0;
    for (auto c : subject) { 
        if (c == search) ++count;
    }

    size_t idx = subject.size()-1 + count * replace.size()-1;
    subject.resize(idx + 1, '\0');

    string reverseReplace{ replace };
    reverse(reverseReplace.begin(), reverseReplace.end());  

    char *end_ptr = &subject[initSize - 1];
    while (end_ptr >= &subject[0])
    {
        if (*end_ptr == search) {
            for (auto c : reverseReplace) {
                subject[idx - 1] = c;
                --idx;              
            }           
        }
        else {
            subject[idx - 1] = *end_ptr;
            --idx;
        }
        --end_ptr;
    }
}

int main()
{
    string s{ "Mr John Smith" };
    ReplaceString(s, ' ', "%20");
    cout << s << "\n";

}

1
std::string replace(std::string base, const std::string from, const std::string to) {
    std::string SecureCopy = base;

    for (size_t start_pos = SecureCopy.find(from); start_pos != std::string::npos; start_pos = SecureCopy.find(from,start_pos))
    {
        SecureCopy.replace(start_pos, from.length(), to);
    }

    return SecureCopy;
}

2
Können Sie diesen Code bitte erklären (in Ihrer Antwort)? Auf diese Weise erhalten Sie möglicherweise mehr positive Stimmen!
Der Kerl mit dem Hut

1

Meine eigene Implementierung, bei der berücksichtigt wird, dass die Größe der Zeichenfolge nur einmal geändert werden muss, kann dann ersetzt werden.

template <typename T>
std::basic_string<T> replaceAll(const std::basic_string<T>& s, const T* from, const T* to)
{
    auto length = std::char_traits<T>::length;
    size_t toLen = length(to), fromLen = length(from), delta = toLen - fromLen;
    bool pass = false;
    std::string ns = s;

    size_t newLen = ns.length();

    for (bool estimate : { true, false })
    {
        size_t pos = 0;

        for (; (pos = ns.find(from, pos)) != std::string::npos; pos++)
        {
            if (estimate)
            {
                newLen += delta;
                pos += fromLen;
            }
            else
            {
                ns.replace(pos, fromLen, to);
                pos += delta;
            }
        }

        if (estimate)
            ns.resize(newLen);
    }

    return ns;
}

Die Verwendung könnte beispielsweise so sein:

std::string dirSuite = replaceAll(replaceAll(relPath.parent_path().u8string(), "\\", "/"), ":", "");

0

Ich lerne gerade C ++, aber wenn ich einen Teil des zuvor veröffentlichten Codes bearbeite, würde ich wahrscheinlich so etwas verwenden. Dies gibt Ihnen die Flexibilität, eine oder mehrere Instanzen zu ersetzen, und Sie können auch den Startpunkt angeben.

using namespace std;

// returns number of replacements made in string
long strReplace(string& str, const string& from, const string& to, size_t start = 0, long count = -1) {
    if (from.empty()) return 0;

    size_t startpos = str.find(from, start);
    long replaceCount = 0;

    while (startpos != string::npos){
        str.replace(startpos, from.length(), to);
        startpos += to.length();
        replaceCount++;

        if (count > 0 && replaceCount >= count) break;
        startpos = str.find(from, startpos);
    }

    return replaceCount;
}

0

Dies könnte noch besser zu bedienen sein

void replace(string& input, const string& from, const string& to)
{
    while(true)
    {
        size_t startPosition = input.find(from);
        if(startPosition == string::npos)
            break;
        input.replace(startPosition, from.length(), to);
    }
}
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.