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.

Tom Leese
quelle
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'
    }
}
Michael Mrozek
quelle
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.

Tom
quelle
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().

SC Madsen
quelle
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
Zarek Tomczak
quelle
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.

Drew Noakes
quelle
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.

Galik
quelle
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)

Maxoumime
quelle
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;
Volomike
quelle
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);
user3016543
quelle
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";

}
Damian
quelle
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;
}
Lucas Civali
quelle
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(), "\\", "/"), ":", "");
TarmoPikaro
quelle
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;
}
irgendein Programmierer
quelle
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);
    }
}
Yashwanth Kumar
quelle