Reemplazar la subcadena con otra subcadena C ++


91

¿Cómo podría reemplazar una subcadena en una cadena con otra subcadena en C ++, qué funciones podría usar?

eg: string test = "abc def abc def";
test.replace("abc", "hij").replace("def", "klm"); //replace occurrence of abc and def with other substring

5
Prácticamente un duplicado de stackoverflow.com/questions/3418231/… que tiene una solución más sólida en la respuesta aceptada.
Dave-Holm

Respuestas:


77

No hay una función incorporada en C ++ para hacer esto. Si desea reemplazar todas las instancias de una subcadena con otra, puede hacerlo mezclando llamadas a string::findy string::replace. Por ejemplo:

size_t index = 0;
while (true) {
     /* Locate the substring to replace. */
     index = str.find("abc", index);
     if (index == std::string::npos) break;

     /* Make the replacement. */
     str.replace(index, 3, "def");

     /* Advance index forward so the next iteration doesn't pick it up as well. */
     index += 3;
}

En la última línea de este código, he incrementado indexla longitud de la cadena que se ha insertado en la cadena. En este ejemplo particular, reemplazar "abc"con "def", esto no es realmente necesario. Sin embargo, en una configuración más general, es importante omitir la cadena que se acaba de reemplazar. Por ejemplo, si desea reemplazar "abc"con "abcabc", sin omitir el segmento de cadena recién reemplazado, este código reemplazaría continuamente partes de las cadenas recién reemplazadas hasta que se agote la memoria. Independientemente, podría ser un poco más rápido omitir esos nuevos personajes de todos modos, ya que hacerlo ahorra algo de tiempo y esfuerzo en la string::findfunción.

¡Espero que esto ayude!


6
No creo que deba incrementar el índice porque ya ha reemplazado los datos para que no los recoja de todos modos.
rossb83

1
@Aidiakapi Si esto se convierte en una función de propósito general, no se atascará en un bucle infinito porque avanza la posición de búsqueda ( index) más allá de la parte de la cadena que fue reemplazada.
Tim R.

1
@TimR. Tienes razón, estaba respondiendo a rossb83 que afirma que el incremento del índice es innecesario. Solo estaba tratando de evitar la desinformación. Así que para todos los demás: El aumento del índice de la longitud de la cadena reemplazado (en este caso 3) es necesario . No lo elimine del código de muestra.
Aidiakapi

@FrozenKiwi Me sorprende escuchar eso. ¿Estás seguro de que ese es el caso?
templatetypedef

1
@JulianCienfuegos Acabo de actualizar la respuesta para abordar esto, ¡gracias por señalarlo! (Además, Aidiakapi es otra persona ... no estoy seguro de quién es.)
templatetypedef

68

Aumento de la biblioteca de algoritmos de cadena de forma:

#include <boost/algorithm/string/replace.hpp>

{ // 1. 
  string test = "abc def abc def";
  boost::replace_all(test, "abc", "hij");
  boost::replace_all(test, "def", "klm");
}


{ // 2.
  string test = boost::replace_all_copy
  (  boost::replace_all_copy<string>("abc def abc def", "abc", "hij")
  ,  "def"
  ,  "klm"
  );
}

5
Arrendajo. Necesito un impulso para reemplazar todas las subcadenas.
Johannes Overmann

2
El impulso es sobre todo una exageración.
Konrad


43

Creo que todas las soluciones fallarán si la longitud de la cadena de reemplazo es diferente de la longitud de la cadena a reemplazar. (busque "abc" y reemplácelo por "xxxxxx") Un enfoque general podría ser:

void replaceAll( string &s, const string &search, const string &replace ) {
    for( size_t pos = 0; ; pos += replace.length() ) {
        // Locate the substring to replace
        pos = s.find( search, pos );
        if( pos == string::npos ) break;
        // Replace by erasing and inserting
        s.erase( pos, search.length() );
        s.insert( pos, replace );
    }
}

41
str.replace(str.find(str2),str2.length(),str3);

Dónde

  • str es la cadena base
  • str2 es la subcadena para encontrar
  • str3 es la subcadena de reemplazo

3
Esto solo reemplaza la primera aparición, ¿no?
jpo38

4
Sugeriría asegurarse de que el resultado de str.find (str2) no sea igual a std :: string :: npos auto found = str.find (str2); if (encontrado! = std :: string :: npos) str.replace (found, str2.length (), str3);
Geoff Lentsch

1
No tenía la intención de escribir la aplicación completa con esto, pero sin ninguna verificación en la entrada, hay casos de esto que no están definidos ...
Jeff Zacher

19

Reemplazar subcadenas no debería ser tan difícil.

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

Si necesita rendimiento, aquí hay una función optimizada que modifica la cadena de entrada, no crea una copia de la cadena:

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

Pruebas:

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;

Salida:

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

es necesario agregar un cheque if (search.empty()) { return; }para evitar un bucle infinito cuando se pasa una 'búsqueda' vacía.
Programador de iOS

Intenté la función ReplaceString, no funcionó. Pero responde a continuación: str.replace (str.find (str2), str2.length (), str3); simplemente simple y funciona bien.
KAMIKAZE

5
using std::string;

string string_replace( string src, string const& target, string const& repl)
{
    // handle error situations/trivial cases

    if (target.length() == 0) {
        // searching for a match to the empty string will result in 
        //  an infinite loop
        //  it might make sense to throw an exception for this case
        return src;
    }

    if (src.length() == 0) {
        return src;  // nothing to match against
    }

    size_t idx = 0;

    for (;;) {
        idx = src.find( target, idx);
        if (idx == string::npos)  break;

        src.replace( idx, target.length(), repl);
        idx += repl.length();
    }

    return src;
}

Como no es miembro de la stringclase, no permite una sintaxis tan agradable como en su ejemplo, pero lo siguiente hará el equivalente:

test = string_replace( string_replace( test, "abc", "hij"), "def", "klm")

3

Generalizando la respuesta de rotmax, aquí hay una solución completa para buscar y reemplazar todas las instancias en una cadena. Si ambas subcadenas son de diferente tamaño, la subcadena se reemplaza usando string :: erase y string :: insert., De lo contrario, se usa la string :: replace más rápida.

void FindReplace(string& line, string& oldString, string& newString) {
  const size_t oldSize = oldString.length();

  // do nothing if line is shorter than the string to find
  if( oldSize > line.length() ) return;

  const size_t newSize = newString.length();
  for( size_t pos = 0; ; pos += newSize ) {
    // Locate the substring to replace
    pos = line.find( oldString, pos );
    if( pos == string::npos ) return;
    if( oldSize == newSize ) {
      // if they're same size, use std::string::replace
      line.replace( pos, oldSize, newString );
    } else {
      // if not same size, replace by erasing and inserting
      line.erase( pos, oldSize );
      line.insert( pos, newString );
    }
  }
}

2

Si está seguro de que la subcadena requerida está presente en la cadena, esto reemplazará la primera aparición de "abc"to"hij"

test.replace( test.find("abc"), 3, "hij");

Se bloqueará si no tiene "abc" en la prueba, así que úselo con cuidado.


1

Aquí hay una solución que escribí usando la táctica del constructor:

#include <string>
#include <sstream>

using std::string;
using std::stringstream;

string stringReplace (const string& source,
                      const string& toReplace,
                      const string& replaceWith)
{
  size_t pos = 0;
  size_t cursor = 0;
  int repLen = toReplace.length();
  stringstream builder;

  do
  {
    pos = source.find(toReplace, cursor);

    if (string::npos != pos)
    {
        //copy up to the match, then append the replacement
        builder << source.substr(cursor, pos - cursor);
        builder << replaceWith;

        // skip past the match 
        cursor = pos + repLen;
    }
  } 
  while (string::npos != pos);

  //copy the remainder
  builder << source.substr(cursor);

  return (builder.str());
}

Pruebas:

void addTestResult (const string&& testId, bool pass)
{
  ...
}

void testStringReplace()
{
    string source = "123456789012345678901234567890";
    string toReplace = "567";
    string replaceWith = "abcd";
    string result = stringReplace (source, toReplace, replaceWith);
    string expected = "1234abcd8901234abcd8901234abcd890";

    bool pass = (0 == result.compare(expected));
    addTestResult("567", pass);


    source = "123456789012345678901234567890";
    toReplace = "123";
    replaceWith = "-";
    result = stringReplace(source, toReplace, replaceWith);
    expected = "-4567890-4567890-4567890";

    pass = (0 == result.compare(expected));
    addTestResult("start", pass);


    source = "123456789012345678901234567890";
    toReplace = "0";
    replaceWith = "";
    result = stringReplace(source, toReplace, replaceWith);
    expected = "123456789123456789123456789"; 

    pass = (0 == result.compare(expected));
    addTestResult("end", pass);


    source = "123123456789012345678901234567890";
    toReplace = "123";
    replaceWith = "-";
    result = stringReplace(source, toReplace, replaceWith);
    expected = "--4567890-4567890-4567890";

    pass = (0 == result.compare(expected));
    addTestResult("concat", pass);


    source = "1232323323123456789012345678901234567890";
    toReplace = "323";
    replaceWith = "-";
    result = stringReplace(source, toReplace, replaceWith);
    expected = "12-23-123456789012345678901234567890";

    pass = (0 == result.compare(expected));
    addTestResult("interleaved", pass);



    source = "1232323323123456789012345678901234567890";
    toReplace = "===";
    replaceWith = "-";
    result = utils_stringReplace(source, toReplace, replaceWith);
    expected = source;

    pass = (0 == result.compare(expected));
    addTestResult("no match", pass);

}

0
    string & replace(string & subj, string old, string neu)
    {
        size_t uiui = subj.find(old);
        if (uiui != string::npos)
        {
           subj.erase(uiui, old.size());
           subj.insert(uiui, neu);
        }
        return subj;
    }

¡Creo que esto se ajusta a sus requisitos con pocos códigos!


No está tomando en consideración múltiples ocurrencias / reemplazos
Elias Bachaalany

0

la versión mejorada de @Czarek Tomczak.
permitir ambos std::stringy std::wstring.

template <typename charType>
void ReplaceSubstring(std::basic_string<charType>& subject,
    const std::basic_string<charType>& search,
    const std::basic_string<charType>& replace)
{
    if (search.empty()) { return; }
    typename std::basic_string<charType>::size_type pos = 0;
    while((pos = subject.find(search, pos)) != std::basic_string<charType>::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
}

0
std::string replace(const std::string & in
                  , const std::string & from
                  , const std::string & to){
  if(from.size() == 0 ) return in;
  std::string out = "";
  std::string tmp = "";
  for(int i = 0, ii = -1; i < in.size(); ++i) {
    // change ii
    if     ( ii <  0 &&  from[0] == in[i] )  {
      ii  = 0;
      tmp = from[0]; 
    } else if( ii >= 0 && ii < from.size()-1 )  {
      ii ++ ;
      tmp = tmp + in[i];
      if(from[ii] == in[i]) {
      } else {
        out = out + tmp;
        tmp = "";
        ii = -1;
      }
    } else {
      out = out + in[i];
    }
    if( tmp == from ) {
      out = out + to;
      tmp = "";
      ii = -1;
    }
  }
  return out;
};

0

Aquí hay una solución que usa la recursividad que reemplaza todas las apariciones de una subcadena con otra subcadena. Esto funciona sin importar el tamaño de las cuerdas.

std::string ReplaceString(const std::string source_string, const std::string old_substring, const std::string new_substring)
{
    // Can't replace nothing.
    if (old_substring.empty())
        return source_string;

    // Find the first occurrence of the substring we want to replace.
    size_t substring_position = source_string.find(old_substring);

    // If not found, there is nothing to replace.
    if (substring_position == std::string::npos)
        return source_string;

    // Return the part of the source string until the first occurance of the old substring + the new replacement substring + the result of the same function on the remainder.
    return source_string.substr(0,substring_position) + new_substring + ReplaceString(source_string.substr(substring_position + old_substring.length(),source_string.length() - (substring_position + old_substring.length())), old_substring, new_substring);
}

Ejemplo de uso:

std::string my_cpp_string = "This string is unmodified. You heard me right, it's unmodified.";
std::cout << "The original C++ string is:\n" << my_cpp_string << std::endl;
my_cpp_string = ReplaceString(my_cpp_string, "unmodified", "modified");
std::cout << "The final C++ string is:\n" << my_cpp_string << std::endl;

0
std::string replace(std::string str, std::string substr1, std::string substr2)
{
    for (size_t index = str.find(substr1, 0); index != std::string::npos && substr1.length(); index = str.find(substr1, index + substr2.length() ) )
        str.replace(index, substr1.length(), substr2);
    return str;
}

Solución corta donde no necesita bibliotecas adicionales.


1
Hay otras 14 respuestas a esta pregunta. ¿Por qué no ofrecer una explicación de por qué el suyo es mejor?
chb

0
std::string replace(std::string str, const std::string& sub1, const std::string& sub2)
{
    if (sub1.empty())
        return str;

    std::size_t pos;
    while ((pos = str.find(sub1)) != std::string::npos)
        str.replace(pos, sub1.size(), sub2);

    return str;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.