¿Cómo busco / busco y reemplazo en una cadena estándar?

94

¿Hay alguna forma de reemplazar todas las apariciones de una subcadena con otra cadena std::string?

Por ejemplo:

void SomeFunction(std::string& str)
{
   str = str.replace("hello", "world"); //< I'm looking for something nice like this
}
Adam Tegen
fuente

Respuestas:

74

¿Por qué no implementar su propio reemplazo?

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();
  }
}
yves baumes
fuente
3
Estás jugando un poco con la memoria aquí con todas las llamadas a "reemplazar": la complejidad sería n² si eliminas "o" de "ooooooo ... o". Supongo que se puede hacer mejor, pero esta solución tiene el mérito de ser fácil de entender.
Zonko
1
¿Por qué no es un bucle for real, en lugar de un bucle for ofuscado?
Shirik
Estoy acostumbrado a aplicar el principio de "mínima sorpresa". Los bucles for son para uso simple de incremento de índice, la mayor parte del tiempo. Aquí, según yo, un bucle de tiempo es más claro.
yves Baumes
1
@aldo Como regla general, es mejor evitar la complejidad y, por ejemplo, usar expresiones regulares como se menciona en otras respuestas. Pero dependiendo de su necesidad, es posible que desee controlar las dependencias de su proyecto. Un pequeño fragmento de código que hace exactamente lo que necesita, no más, a veces es mejor.
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");

Aquí está la documentación oficial sobre replace_all.

TheNamelessOne
fuente
1
Tenga en cuenta que no tiene que crear explícitamente std :: string's para el patrón y el reemplazo: boost :: replace_all (target, "foo", "bar");
Alexis Wilke
4
+1, con una advertencia: replace_allse producirá un error de segmentación para las versiones de boost> 1.43 en Sun Studio para cualquier versión <12.3
Brian Vandenberg
3
boostaumenta considerablemente el tiempo de compilación en dispositivos integrados. Incluso ARMv7 de cuatro núcleos. Se compilan 100 líneas de código en 2 minutos, sin impulso, 2 segundos.
Piotr Kula
4
@ppumkin: eso significa que su compilador (o configuración de compilación, o lo que sea) apesta, no la arquitectura de destino, que no tiene nada que ver con eso.
Daniel Kamil Kozar
Si su compilador admite un encabezado precompilado, se recomienda encarecidamente usarlo cuando use boost. Realmente ahorra tiempo.
Alexey Omelchenko
33

En C ++ 11, puede hacer esto como una línea con una llamada 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;

salida:

Removeallspaces
Brent Bradburn
fuente
¡Gracias, muy fácil de usar y recordar!
Julian Declercq
Tenga en cuenta también que frompuede ser una expresión regular, por lo que puede utilizar criterios de coincidencia más sofisticados si es necesario. Lo que no veo es cómo hacer esto sin aplicar alguna forma de análisis de expresiones regulares, en lugar de usar solo una interpretación directa de los fromcaracteres.
Brent Bradburn
Esto puede requerir un compilador actualizado. Funcionó con gcc 5.0, pero tuve algunos problemas con gcc 4.8.4.
Brent Bradburn
@nobar, sí, si recuerdo bien, el soporte de expresiones regulares en 4.8.x no estaba completo. También puede tener búsquedas más sofisticadas, pero se penaliza el tiempo ... Será más lento que las otras funciones de búsqueda y reemplazo más sencillas.
Alexis Wilke
2
Tenga en cuenta que esto solo funcionará para caracteres alfanuméricos muy básicos y nada más sin hacer mucho preprocesamiento según el tipo de cadena. Todavía no he encontrado un reemplazo de cadena basado en expresiones regulares de propósito general.
Piyush Soni
17

¿Por qué no devolver una cadena modificada?

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
Czarek Tomczak
fuente
6

Mi búsqueda y reemplazo en línea en el lugar con plantilla:

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

Devuelve un recuento del número de elementos sustituidos (para usar si desea ejecutar esto sucesivamente, etc.). Para usarlo:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");
Marius
fuente
4
Probé esta muestra en GCC pero no se compilaba; no le gustó el uso de T :: size_t. Reemplazar T :: size_t con typename T :: size_type soluciona el problema.
Andrew Wyatt
3

La forma más fácil (ofreciendo algo parecido a lo que escribió) es usar Boost.Regex , específicamente regex_replace .

std :: string tiene incorporados los métodos find () y replace (), pero es más complicado trabajar con ellos ya que requieren lidiar con índices y longitudes de cadenas.

Alan
fuente
3
También están los algoritmos de cadena de refuerzo, incluido replace_all (la expresión regular puede ser un poco pesada para una sustitución tan simple).
UncleBens
3

Creo que esto funcionaría. Toma const char * como parámetro.

//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; 
   }
}
Adam Tegen
fuente
Dado que size_typepara una cadena es unsigned, su >=verificación en la condición de bucle siempre será true. Tienes que usarlo std::string::nposallí.
Pavel Minaev
size_type no está sin firmar. No está firmado en muchas plataformas, pero no en todas.
Alan
12
¿Por qué en el mundo esto no es parte de std :: string? ¿Existe alguna otra clase String seria en el mundo de la programación que no ofrezca una operación de 'buscar y reemplazar'? Seguramente es más común que tener dos iteradores y querer reemplazar el texto entre ellos. A veces, std :: string se siente como un automóvil con un parabrisas de espectro sintonizable pero sin forma de bajar la ventanilla del conductor.
Spike0xff
@ Spike0xff boost tieneroll_down_window
ta.speot.es el
1
@gustafr: Mi error. He trabajado en sistemas donde los compiladores más antiguos definieron size_t incorrectamente.
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;
}
Björn Ganster
fuente
1
Solo necesitamos buscar nuevas coincidencias desde la última coincidencia, por eso el algoritmo rastrea cuidadosamente la última coincidencia en pos. pos2 siempre almacena la siguiente coincidencia, por lo que concatenamos la cadena entre pos y pos2 al resultado, luego avanzamos pos y pos2. Si no se puede encontrar otra coincidencia, concatenamos el resto de la cadena al resultado.
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();
  }
}

La comprobación de que oldStr esté vacía es importante. Si por alguna razón ese parámetro está vacío, se quedará atascado en un bucle infinito.

Pero sí, use la solución probada C ++ 11 o Boost si puede.

ericcurtin
fuente