Come faccio a cercare / trovare e sostituire in una stringa standard?


Risposte:


74

Perché non implementare la tua sostituzione?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}

3
Stai un po 'scherzando con la memoria qui con tutte le chiamate a "sostituire": la complessità sarebbe n² se rimuovi "o" da "ooooooo ... o". Immagino si possa fare di meglio, ma questa soluzione ha il pregio di essere di facile comprensione.
Zonko

1
Perché questo non è un ciclo for effettivo, piuttosto che un ciclo for offuscato?
Shirik

Sono abituato ad applicare il principio della "minima sorpresa". I cicli For sono per un semplice incremento dell'indice, la maggior parte del tempo. Qui, secondo me, un ciclo while è più chiaro.
yves Baumes

1
@aldo Come regola generale è meglio evitare la complessità e, ad esempio, utilizzare regex come menzionato in altre risposte. Ma a seconda delle tue necessità potresti voler controllare le dipendenze del tuo progetto. Un piccolo frammento di codice che fa esattamente ciò di cui hai bisogno, non di più, a volte è meglio.
yves Baumes

158
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");

Ecco la documentazione ufficiale su replace_all.


1
Nota che non devi creare esplicitamente std :: string's per il pattern e la sostituzione: boost :: replace_all (target, "foo", "bar");
Alexis Wilke

4
+1, con un avvertimento: replace_allsegfault per le versioni boost> 1.43 su Sun Studio per qualsiasi versione <12.3
Brian Vandenberg

3
boostaumenta notevolmente il tempo di compilazione sui dispositivi embedded. Anche ARMv7 quad core. 100 righe di codice vengono compilate in 2 minuti, senza boost, 2 secondi.
Piotr Kula

4
@ppumkin: questo significa che il tuo compilatore (o configurazione di build, o qualsiasi altra cosa) fa schifo, non l'architettura di destinazione, che non ha nulla a che fare con esso.
Daniel Kamil Kozar

Se il tuo compilatore supporta l'intestazione precompilata, è altamente raccomandato usarlo quando usi boost. Fa davvero risparmiare tempo.
Alexey Omelchenko

33

In C ++ 11, puoi farlo come una riga con una chiamata a regex_replace:

#include <string>
#include <regex>

using std::string;

string do_replace( string const & in, string const & from, string const & to )
{
  return std::regex_replace( in, std::regex(from), to );
}

string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;

produzione:

Removeallspaces

Grazie, molto facile da usare e da ricordare!
Julian Declercq

Nota anche che frompuò essere un'espressione regolare, quindi puoi utilizzare criteri di corrispondenza più sofisticati se necessario. Quello che non vedo è come farlo senza applicare una qualche forma di analisi di espressioni regolari, usando invece solo un'interpretazione diretta dei fromcaratteri.
Brent Bradburn

Ciò potrebbe richiedere un compilatore aggiornato. Ha funzionato con gcc 5.0, ma ho avuto alcuni problemi con gcc 4.8.4.
Brent Bradburn

@nobar, sì, se ricordo bene il supporto alle regex in 4.8.x non era completo. Inoltre puoi avere ricerche più sofisticate, ma sei penalizzato dal punto di vista del tempo ... Sarà più lento delle altre funzioni di ricerca e sostituzione più dirette.
Alexis Wilke

2
Si noti che questo funzionerà solo per caratteri alfanumerici di base e nient'altro senza eseguire molta preelaborazione a seconda del tipo di stringa. Non ho ancora trovato una sostituzione di stringhe basata su espressioni regolari generali.
Piyush Soni

17

Perché non restituire una stringa modificata?

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 changed: " 
          << 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

6

Trova e sostituisci sul posto inline basato su modelli:

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

Restituisce un conteggio del numero di elementi sostituiti (da utilizzare se si desidera eseguirlo successivamente, ecc.). Per usarlo:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");

4
Ho provato questo esempio con GCC ma non si è compilato - non gli piaceva l'uso di T :: size_t. La sostituzione di T :: size_t con typename T :: size_type risolve il problema.
Andrew Wyatt

3

Il modo più semplice (offrendo qualcosa di simile a quello che hai scritto) è usare Boost.Regex , in particolare regex_replace .

std :: string ha i metodi find () e replace () incorporati, ma è più complicato lavorare con loro poiché richiedono la gestione degli indici e delle lunghezze delle stringhe.


3
Ci sono anche gli algoritmi della stringa boost, incluso replace_all (regex potrebbe essere un po 'pesante per una sostituzione così semplice).
UncleBens

3

Credo che funzionerebbe. Accetta i const char * come parametro.

//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
   //ASSERT(find != NULL);
   //ASSERT(replace != NULL);
   size_t findLen = strlen(find);
   size_t replaceLen = strlen(replace);
   size_t pos = 0;

   //search for the next occurrence of find within source
   while ((pos = source.find(find, pos)) != std::string::npos)
   {
      //replace the found string with the replacement
      source.replace( pos, findLen, replace );

      //the next line keeps you from searching your replace string, 
      //so your could replace "hello" with "hello world" 
      //and not have it blow chunks.
      pos += replaceLen; 
   }
}

Dato che size_typeper una stringa è unsigned, il >=controllo nella condizione del ciclo sarà sempre true. Devi usare std::string::nposlì.
Pavel Minaev,

size_type non è senza segno. Non è firmato su molte piattaforme, ma non su tutte.
Alan,

12
Perché nel mondo questo non fa parte di std :: string? C'è qualche altra seria classe String nel mondo della programmazione che non offre un'operazione di "trova e sostituisci"? Sicuramente è più comune che avere due iteratori e voler sostituire il testo tra di loro ?? A volte std :: string sembra un'auto con un parabrezza a spettro regolabile ma non c'è modo di abbassare il finestrino del conducente.
Spike0xff

@ Spike0xff boost hasroll_down_window
ta.speot.is

1
@gustafr: errore mio. Ho lavorato su sistemi in cui i vecchi compilatori definivano size_t in modo improprio.
Alan

1
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates 
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr, 
    const std::string& replacer)
{
    // Prevent an infinite loop if the input is empty
    if (searchStr == "") {
        return str;
    }

    std::string result = "";
    size_t pos = 0;
    size_t pos2 = str.find(searchStr, pos);

    while (pos2 != std::string::npos) {
        result += str.substr(pos, pos2-pos) + replacer;
        pos = pos2 + searchStr.length();
        pos2 = str.find(searchStr, pos);
    }

    result += str.substr(pos, str.length()-pos);
    return result;
}

1
Dobbiamo solo cercare nuove corrispondenze dall'ultima corrispondenza, ecco perché l'algoritmo traccia attentamente l'ultima partita in pos. pos2 memorizza sempre la corrispondenza successiva, quindi concateniamo la stringa tra pos e pos2 al risultato, quindi avanziamo pos e pos2. Se non è possibile trovare altre corrispondenze, concateniamo il resto della stringa per ottenere il risultato.
Björn Ganster

1
#include <string>

using std::string;

void myReplace(string& str,
               const string& oldStr,
               const string& newStr) {
  if (oldStr.empty()) {
    return;
  }

  for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
    str.replace(pos, oldStr.length(), newStr);
    pos += newStr.length();
  }
}

Il controllo per oldStr è vuoto è importante. Se per qualsiasi motivo quel parametro è vuoto, rimarrai bloccato in un ciclo infinito.

Ma sì, usa la soluzione collaudata C ++ 11 o Boost se puoi.

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.