Sostituisci parte di una stringa con un'altra stringa


186

È possibile in C ++ sostituire parte di una stringa con un'altra stringa?

Fondamentalmente, vorrei fare questo:

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

Ma vorrei usare le librerie C ++ standard.


1
possibile duplicato di Qual è la funzione per sostituire la stringa in C? - oops mi dispiace che sia C, non C ++; Vorrei poter annullare la votazione.
poligenelubrificanti

1
@poly Penso che anche questo deve essere stato richiesto per il C ++, ma non riesco a trovarlo
Michael Mrozek,

1
C'è un tag std sulla domanda, ma forse potresti essere interessato agli algoritmi di stringa di boost, che include anche una vasta scelta di algoritmi di sostituzione (inplace / copy, case sensitive / case insensitive, sostituisci first / last / all / n-th ).
UncleBens,

@Michael Mrozek Ce n'è uno su stackoverflow.com/questions/3418231/… ma è più recente e il tuo metodo di sostituzione Tutto qui è più robusto.
dave-holm,

Risposte:


288

C'è una funzione per trovare una sottostringa all'interno di una stringa ( find) e una funzione per sostituire un intervallo particolare in una stringa con un'altra stringa ( replace), in modo da poterle combinare per ottenere l'effetto desiderato:

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

In risposta a un commento, penso replaceAllche probabilmente sarebbe simile a questo:

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
Come lo riparerei se la stringa originale avesse più di un'istanza di "$ name" e volessi sostituirli tutti.
Tom Leese,

1
Perché non sono frome topassati per constriferimento? Cosa fa la tua funzione se fromnon c'è? -1da parte mia per quello.
sabato

10
@sbi Risolto, anche se avresti potuto dirlo come consigli invece di attacchi - semplicemente non mi è venuto in mente, penso raramente di usare conste se avessi scritto un metodo di utilità come questo lo chiamerei solo se sapessi il la sostituzione era valida
Michael Mrozek il

10
@Michael: Bene, ho trasformato il mio voto negativo in un voto positivo. Ignorare constè ignorare uno dei migliori strumenti del C ++. Il passaggio per constriferimento dovrebbe essere la modalità predefinita per i parametri della funzione. (FTR, senza il const, non potresti nemmeno passare letterali di stringa alla tua funzione, perché non puoi legare i provvisori ai non constriferimenti. Quindi la funzione non farebbe nemmeno quello per cui era stata scritta.)
sbi

19
È ancora l'unica soluzione nel 2018? Se è così e qualsiasi comitato C ++ sta leggendo questo, risolverlo. È imbarazzante. dividere (stringa, stringa) e sostituire (stringa, stringa) per favore!
user997112

96

Con C ++ 11 puoi usare std::regexcosì:

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

La doppia barra rovesciata è necessaria per sfuggire a un personaggio di fuga.


Sono abbastanza sicuro std::regex_replaceche non accetta la stringa di Qt.
BartoszKP,

1
Hai ragione. Come succede, QString fornisce un metodo di sostituzione che accetta un QRexExp, permettendo di usare le cose di Qt. Ma penso che la risposta attuale possa essere corretta sostituendola stringcon string.toStdString().
Tom,

1
O semplicemente cambiando Stringin std::string, perché la domanda non è correlata a Qt. Per favore, considera di farlo - Sarò lieto di votare la tua risposta dopo.
BartoszKP,

5
La stringa non elaborata consente di scrivere R"(\$name)"anziché "\\$name".
Jarod42,

2
Quanto sarà più lento di trovare / sostituire senza considerare il tempo di costruzione di std :: regex?
jw_

18

std::stringha un replacemetodo, è quello che stai cercando?

Puoi provare:

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

Non ho provato me stesso, basta leggere la documentazione su find()e replace().


2
Da quello che posso vedere il metodo di sostituzione std :: string non prende due stringhe come vorrei.
Tom Leese,

2
Questo non funziona per me. sizeof dovrebbe essere sostituito da string ("Somename"). size () - 1
TimZaman

@TimZaman: Questo mi confonde, la documentazione afferma chiaramente che puoi inizializzare da una stringa in stile C.
SC Madsen,

5
il secondo argomento dovrebbe essere la lunghezza di "$ name" (anziché la lunghezza di "Somename"), non è vero?
Daniel Kiss,

10

Per ottenere la nuova stringa restituita, utilizzare questo:

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

Se hai bisogno di prestazioni, ecco una funzione ottimizzata che modifica la stringa di input, non crea una copia della stringa:

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

test:

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;

Produzione:

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

La tua chiamata a subject.replace in ReplaceStringInPlace () sta davvero modificando la stringa sul posto?
Damian,

Ho guardato brevemente l'origine e sembra che usi la semantica di spostamento per spostare la parte anteriore della vecchia stringa in posizione, in modo che non venga copiata, ma il nuovo pezzo inserito viene copiato nella vecchia stringa e nella coda della vecchia stringa viene copiato nel buffer ridimensionato della vecchia stringa. È possibile che la stringa si espanda così tanto che l'intero buffer sottostante viene riallocato, ma se si sostituisce 1 a 1 come nel suo esempio, penso che si verifichi "sul posto" o senza alcuna copia, ma se si espande la stringa, solo la prima parte della vecchia stringa non viene copiata, e solo forse allora.
Motes,

6

Sì, puoi farlo, ma devi trovare la posizione della prima stringa con il membro find () della stringa, e quindi sostituire con il suo membro replace ().

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

Se stai pensando di utilizzare la Libreria standard, dovresti davvero procurarti una copia del libro The C ++ Standard Library che copre molto bene tutto ciò.


1
esso è size_t e non size_type
Revo

1
È std :: string :: size_type, non size_t o il size_type senza ornamenti.
jmucchiello,

5

Uso generalmente questo:

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

Chiama ripetutamente std::string::find()per individuare altre occorrenze della stringa cercata fino a quando std::string::find()non trova nulla. Poiché std::string::find()restituisce la posizione della corrispondenza, non abbiamo il problema di invalidare gli iteratori.


4

Sembra un'opzione

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

Potresti racchiuderlo in una funzione ma questa soluzione a una riga sembra accettabile. Il problema è che questo cambierà solo la prima occorrenza, potresti volerci passare sopra, ma ti permette anche di inserire diverse variabili in questa stringa con lo stesso token ( %s)


1
Mi piace lo stile ma ho trovato confuse le diverse corde ^^str.replace(str.find("%s"), string("%s").size(), "Something");
Paul Würtz,

3

Se tutte le stringhe sono std :: string, troverai strani problemi con il taglio dei caratteri se usato sizeof()perché è pensato per stringhe C, non per stringhe C ++. La correzione consiste nell'utilizzare il .size()metodo di classe di std::string.

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

Ciò sostituisce sHaystack inline - non è necessario eseguire nuovamente una = assegnazione.

Esempio di utilizzo:

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

Se si desidera farlo rapidamente, è possibile utilizzare un approccio a due scansioni. Pseudo codice:

  1. prima analisi. trova quanti caratteri corrispondenti.
  2. espandere la lunghezza della stringa.
  3. seconda analisi. Inizia dalla fine della stringa quando otteniamo una corrispondenza che sostituiamo, altrimenti copiamo solo i caratteri dalla prima stringa.

Non sono sicuro che questo possa essere ottimizzato per un algoritmo sul posto.

E un esempio di codice C ++ 11 ma cerco solo un carattere.

#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
Puoi per favore spiegare questo codice (nella tua risposta)? In questo modo potresti ottenere più voti!
The Guy with The Hat,

1

La mia implementazione, tenendo conto del fatto che la stringa deve essere ridimensionata una sola volta, quindi può avvenire la sostituzione.

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

L'uso potrebbe essere ad esempio così:

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

0

Sto imparando solo ora il C ++, ma modificando parte del codice precedentemente pubblicato, probabilmente userò qualcosa del genere. Ciò ti dà la flessibilità di sostituire 1 o più istanze e ti consente anche di specificare il punto iniziale.

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

Questo potrebbe essere ancora meglio da usare

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