Come sostituire tutte le occorrenze di un carattere nella stringa?


480

Qual è il modo efficace per sostituire tutte le occorrenze di un personaggio con un altro personaggio std::string?

Risposte:


742

std::stringnon contiene tale funzione ma è possibile utilizzare la replacefunzione autonoma dall'intestazione algorithm.

#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'
}

6
std::stringè un contenitore appositamente progettato per funzionare con sequenze di caratteri. link
Kirill V. Lyadvinsky,

164
Sfortunatamente, ciò consente di sostituire solo un carattere con un altro carattere. Non può sostituire un carattere con più caratteri (ovvero da una stringa). C'è un modo per eseguire una ricerca-sostituzione con più caratteri?
SasQ,

6
@Kirill V. Lyadvinsky Cosa succede se desidero solo rimuovere un'occorrenza.
SIFE,

4
@ KirillV.Lyadvinsky: quando uso questo metodo per sostituire tutte le x con le y, il risultato è una lunga stringa y, indipendentemente dalla stringa originale. Sono curioso di sapere quale pensi che sarebbe il problema. (il codice è esattamente lo stesso che hai scritto)
Trascendente il

6
@Transcendent: questo è esattamente ciò che accade std::string::replace()invece di std::replace()! 'x' ( char) viene implicitamente inserito in size_t[valore 120], quindi l'intera stringa o parte di essa verrà riempita con 120 copie di 'y'.
IBue

127

Ho pensato di aggiungere anche la soluzione boost :

#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, "#", "@");

Quindi ti mancano alcuni -Iflag per il tuo compilatore in modo che possa trovare le librerie Boost sul tuo sistema. Forse devi prima installarlo.
Martin Ueding,

Quanto sopra è più efficace poiché esce con std lib.Non tutti usano la libreria boost ;-)
hfrmobile il

122

La domanda è centrata sulla charactersostituzione, ma, poiché ho trovato questa pagina molto utile (in particolare l'osservazione di Konrad ), vorrei condividere questa implementazione più generalizzata, che consente anche di affrontare 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;
}

Uso:

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;

Uscite:

Number_Of_Beans

XXjXugtXty

hhjhugthty


MODIFICARE:

Quanto sopra può essere implementato in un modo più adatto, nel caso in cui le prestazioni siano di tuo interesse, restituendo nulla ( void) ed eseguendo le modifiche direttamente sulla stringa strfornita come argomento, passata per indirizzo anziché per valore . Ciò eviterebbe la copia inutile e costosa della stringa originale, restituendo il risultato. La tua chiamata, quindi ...

Codice :

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

Spero che questo possa essere utile per alcuni altri ...


4
Questo ha un problema di prestazioni nei casi in cui la stringa di origine è grande e ci sono molte occorrenze della stringa da sostituire. string :: replace () verrebbe chiamato più volte, causando molte copie di stringhe. Vedi la mia soluzione che risolve questo problema.
Minastaros,

1
Nit picking ahead: per indirizzo => per riferimento . Che si tratti di un indirizzo o meno, è un dettaglio di implementazione.
Max Truxa,

1
Dovresti effettivamente verificare se la fromstringa è vuota, altrimenti si verificherà un ciclo infinito.
novizio

34

Immagina un grande BLOB binario in cui tutti gli 0x00 byte devono essere sostituiti da "\ 1 \ x30" e tutti gli 0x01 byte da "\ 1 \ x31" perché il protocollo di trasporto non consente \ 0 byte.

Nei casi in cui:

  • la stringa di sostituzione e quella da sostituire hanno lunghezze diverse,
  • ci sono molte occorrenze della stringa da sostituire all'interno della stringa di origine e
  • la stringa di origine è grande,

le soluzioni fornite non possono essere applicate (perché sostituiscono solo caratteri singoli) o hanno un problema di prestazioni, perché chiamerebbero string :: replace diverse volte che genera ripetutamente copie delle dimensioni del BLOB. (Non conosco la soluzione boost, forse è OK da quella prospettiva)

Questo cammina lungo tutte le occorrenze nella stringa sorgente e costruisce la nuova stringa pezzo per pezzo una volta :

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

Questa è di gran lunga la migliore soluzione qui che si basa sul solo STL. Se hai intenzione di inserire una funzione personalizzata per un facile utilizzo ovunque, fallo come questo.
Roger Sanders,

21

Una semplice ricerca e sostituzione per un singolo personaggio sarebbe simile a:

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

Per fare questo per l'intera stringa, la cosa facile da fare sarebbe fare un ciclo fino a quando non s.findinizi a tornare npos. Suppongo che potresti anche prendere range_errorper uscire dal ciclo, ma è un po 'brutto.


7
Sebbene questa sia probabilmente una soluzione adatta quando il numero di caratteri da sostituire è piccolo rispetto alla lunghezza della stringa, non si adatta bene. All'aumentare della proporzione di caratteri nella stringa originale che devono essere sostituiti, questo metodo si avvicinerà a O (N ^ 2) nel tempo.
andand

7
Vero. La mia filosofia generale è quella di fare la cosa facile (scrivere e leggere) fino a quando le inefficienze stanno causando problemi reali. Ci sono alcune circostanze in cui potresti avere stringhe humoungous in cui O (N ** 2) è importante, ma il 99% delle volte le mie stringhe sono 1K o meno.
TED

3
... detto questo, mi piace il metodo di Kirill meglio (e l'ho già votato).
TED

Cosa succede se "x" non viene trovato? Inoltre, perché stai usando doppie parentesi graffe?
Prasath Govind,

@PrasathGovind - Stavo solo mostrando le chiamate richieste (quindi "qualcosa di simile"). Dettagli importanti ma oscuri come la corretta gestione degli errori sono stati lasciati come esercizio per il lettore. Per quanto riguarda le "doppie parentesi graffe", non sono sicuro di cosa siano o di cosa tu stia parlando. Per me un "tutore" è il {personaggio. Non so cosa sia un "doppio tutore". Forse hai qualche tipo di problema con i caratteri?
TED

6

Se stai cercando di sostituire più di un singolo carattere e hai a che fare solo con std::string, allora questo snippet funzionerebbe, sostituendo sNeedle in sHaystack con sReplace, e sNeedle e sReplace non devono avere le stesse dimensioni. Questa routine utilizza il ciclo while per sostituire tutte le occorrenze, anziché solo la prima trovata da sinistra a destra.

while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}

Questo è O (n ^). Potresti farlo in O (n) tempo.
Changming Sun,

3
@ChangmingSun quale soluzione O (n) intendi?
Habakuk,

2
Questo ciclo infinito se kNeedle sembra essere una sottostringa di sReplace.
Prideout

Inoltre c'è una findchiamata due volte. Considera di rendere quel risultato una variabile temporanea.
Luc Bloom,

4

Come suggerito da Kirill, utilizzare il metodo di sostituzione o iterare lungo la stringa sostituendo ogni carattere in modo indipendente.

In alternativa puoi usare il findmetodo o in find_first_ofbase a cosa devi fare. Nessuna di queste soluzioni farà il lavoro in una volta sola, ma con alcune righe di codice in più dovresti farle funzionare per te. :-)


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

Se word è lungo, potrebbe esserci molto overhead durante la chiamata della funzione. È possibile ottimizzare questo passando word, targete replacementcome const riferimenti.
TrebledJ,

2

Che dire di Abseil StrReplaceAll ? Dal file di intestazione:

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

1

Vecchia scuola :-)

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;

Risultato:

H: \ Recursos \ audio \ youtube \ libre \ falta \


0

Questo funziona! Ho usato qualcosa di simile a questo per un'app di libreria, in cui l'inventario è stato archiviato in un CSV (come un file .dat). Ma nel caso di un singolo carattere, il che significa che il sostituto è solo un singolo carattere, ad esempio '|', deve essere racchiuso tra virgolette "|" per non generare un carattere const di conversione non valido.

#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

Al momento non sto usando CentOS, quindi la mia versione del compilatore è sotto. La versione C ++ (g ++), impostazione predefinita C ++ 98:

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.

0

Se sei disposto a usare std::strings, puoi usare la funzione di questa app di strsubesempio così com'è o aggiornarla se vuoi che prenda un tipo o un set di parametri diversi per raggiungere approssimativamente lo stesso obiettivo. Fondamentalmente, utilizza le proprietà e le funzionalità di std::stringper cancellare rapidamente il set di caratteri corrispondente e inserire i caratteri desiderati direttamente all'interno di std::string. Ogni volta che esegue questa operazione di sostituzione, l'offset si aggiorna se riesce ancora a trovare i caratteri corrispondenti da sostituire e, se non è più possibile sostituirlo, restituisce la stringa nel suo stato dall'ultimo aggiornamento.

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

Se non vuoi fare affidamento sull'uso di std::strings come parametri in modo da poter invece passare stringhe in stile C, puoi vedere l'esempio aggiornato di seguito:

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

0

Per situazioni semplici questo funziona abbastanza bene senza usare altre librerie quindi std :: string (che è già in uso).

Sostituisci tutte le occorrenze del carattere a con il carattere 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");
    }
}

Se la stringa è grande o più chiamate da sostituire è un problema, puoi applicare la tecnica menzionata in questa risposta: https://stackoverflow.com/a/29752943/3622300


0

ecco una soluzione che ho lanciato, in uno spirito DRI massimo. cercherà sNeedle in sHaystack e lo sostituirà con sReplace, nTimes se non 0, altrimenti tutte le occorrenze di sNeedle. non cercherà più nel testo sostituito.

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