¿Cómo reemplazar todas las apariciones de un personaje en cadena?

Respuestas:

742

std::stringno contiene dicha función, pero podría usar la replacefunción independiente del algorithmencabezado.

#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'
}
Kirill V. Lyadvinsky
fuente
66
std::stringes un contenedor específicamente diseñado para operar con secuencias de caracteres. enlace
Kirill V. Lyadvinsky
164
Desafortunadamente, esto permite reemplazar solo un personaje por otro personaje. No puede reemplazar un carácter con más caracteres (es decir, por una cadena). ¿Hay alguna manera de hacer una búsqueda de reemplazo con más caracteres?
SasQ
66
@Kirill V. Lyadvinsky ¿Qué pasa si solo quiero eliminar una ocurrencia?
SIFE
44
@ KirillV.Lyadvinsky: Cuando uso este método para reemplazar todas las x por y, el resultado es una larga cadena y sin importar cuál sea la cadena original. Tengo curiosidad por saber cuál sería el problema. (el código es exactamente el mismo que escribió)
Trascendente
66
@Transcendent: ¡Esto es exactamente lo que sucede en std::string::replace()lugar de std::replace()! 'x' ( char) se convierte implícitamente en size_t[valor 120], por lo que toda la cadena o parte de ella se rellenará con 120 copias de 'y'.
IBue
127

Pensé en tirar el solución de impulso :

#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, "#", "@");
UncleZeiv
fuente
Entonces le faltan algunos -Iindicadores para su compilador para que pueda encontrar las bibliotecas Boost en su sistema. Quizás necesite incluso instalarlo primero.
Martin Ueding
Lo anterior es más efectivo ya que sale con std lib. No todos usan la biblioteca boost ;-)
hfrmobile
122

La pregunta se centra en el characterreemplazo, pero, como encontré esta página muy útil (especialmente el comentario de Konrad ), me gustaría compartir esta implementación más generalizada, que también permite tratar 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;

Salidas:

Number_Of_Beans

XXjXugtXty

hhjhugthty


EDITAR:

Lo anterior se puede implementar de una manera más adecuada, en caso de que el rendimiento sea de su interés, devolviendo nada ( void) y realizando los cambios directamente en la cadena strdada como argumento, pasado por dirección en lugar de por valor . Esto evitaría una copia inútil y costosa de la cadena original, al tiempo que devuelve el resultado. Tu llamada, entonces ...

Código:

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

Espero que esto sea útil para otros ...

Gauthier Boaglio
fuente
44
Este tiene un problema de rendimiento en los casos en que la cadena de origen es grande y hay muchas ocurrencias de la cadena que se reemplazará. string :: replace () se llamaría muchas veces, lo que causa muchas copias de cadenas. Vea mi solución que aborda ese problema.
minastaros
1
Selección de Nit por delante: por dirección => por referencia . Si es una dirección o no, es un detalle de implementación.
Max Truxa
1
En realidad, debe verificar si la fromcadena está vacía, de lo contrario se producirá un bucle sin fin.
novato el
34

Imagine un blob binario grande donde todos los 0x00 bytes serán reemplazados por "\ 1 \ x30" y todos los 0x01 bytes por "\ 1 \ x31" porque el protocolo de transporte no permite \ 0-bytes.

En casos donde:

  • la cadena de reemplazo y la cadena de reemplazo tienen diferentes longitudes,
  • hay muchas ocurrencias de la cadena a reemplazar dentro de la cadena de origen y
  • la cadena de origen es grande,

las soluciones proporcionadas no pueden aplicarse (porque reemplazan solo caracteres individuales) o tienen un problema de rendimiento, porque llamarían a string :: replace varias veces, lo que genera copias del tamaño del blob una y otra vez. (No conozco la solución de impulso, tal vez esté bien desde esa perspectiva)

Éste recorre todas las ocurrencias en la cadena de origen y construye la nueva cadena pieza por pieza una vez :

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);
}
minastaros
fuente
Esta es, con mucho, la mejor solución aquí, que se basa solo en el STL. Si va a colocar una función personalizada para usarla fácilmente en cualquier lugar, hágalo esta.
Roger Sanders
21

Un simple buscar y reemplazar un solo personaje sería algo así como:

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

Para hacer esto para toda la cadena, lo más fácil sería hacer un bucle hasta que s.findcomience a regresar npos. Supongo que también podrías atraparlo range_errorpara salir del bucle, pero eso es un poco feo.

TED
fuente
77
Si bien esta es probablemente una solución adecuada cuando el número de caracteres a reemplazar es pequeño en comparación con la longitud de la cadena, no se escala bien. A medida que aumenta la proporción de caracteres en la cadena original que necesitan ser reemplazados, este método se acercará a O (N ^ 2) a tiempo.
andand
77
Cierto. Mi filosofía general es hacer lo fácil (escribir y leer) hasta el momento en que las ineficiencias causen problemas reales. Hay algunas circunstancias en las que puede tener cadenas humoungous donde O (N ** 2) importa, pero el 99% del tiempo mis cadenas son 1K o menos.
TED
3
... Dicho esto, me gusta más el método de Kirill (y ya lo había votado).
TED
¿Qué sucede si no se encuentra "x"? Además, ¿por qué estás usando llaves dobles?
Prasath Govind
@PrasathGovind - Solo estaba mostrando las llamadas requeridas (de ahí "algo así como"). Detalles importantes pero oscuros como el manejo adecuado de errores se dejaron como ejercicio para el lector. En cuanto a "llaves dobles", no estoy seguro de cuáles son, o de qué estás hablando. Para mí una "llave" es el {personaje. No sé qué es un "doble corsé". Tal vez tienes algún tipo de problema de fuente?
TED
6

Si está buscando reemplazar más de un solo personaje, y está tratando solo con std::string, entonces este fragmento funcionaría, reemplazando sNeedle en sHaystack con sReplace, y sNeedle y sReplace no necesitan ser del mismo tamaño. Esta rutina usa el ciclo while para reemplazar todas las ocurrencias, en lugar de solo la primera que se encuentra de izquierda a derecha.

while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}
Volomike
fuente
Esto es O (n ^). Podrías hacerlo en O (n) tiempo.
Changming Sun
3
@ChangmingSun, ¿a qué solución de O (n) te refieres?
Habacuc
2
Esto hará un bucle infinito si kNeedle es una subcadena de sReplace.
Orgulloso
Además hay una findllamada dos veces. Considere hacer de ese resultado una variable temporal.
Luc Bloom
4

Como sugirió Kirill, use el método de reemplazo o itere a lo largo de la cadena reemplazando cada carácter de forma independiente.

Alternativamente, puede usar el findmétodo o find_first_ofdependiendo de lo que necesite hacer. Ninguna de estas soluciones hará el trabajo de una vez, pero con unas pocas líneas adicionales de código debe hacer que funcionen para usted. :-)

Konrad
fuente
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;
}
Lloydie
fuente
Si wordes largo, puede haber mucha sobrecarga al llamar a la función. Puede optimizar esta pasando word, targety replacementcomo const referencias.
TrebledJ
2

¿Qué pasa con Abseil StrReplaceAll ? Desde el archivo de encabezado:

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

Vieja escuela :-)

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;

Resultado:

H: \ recursos \ audio \ youtube \ libre \ falta \

Iván Rodríguez
fuente
0

¡Esto funciona! Usé algo similar a esto para una aplicación de librería, donde el inventario se almacenaba en un archivo CSV (como un archivo .dat). Pero en el caso de un solo carácter, lo que significa que el sustituto es solo un carácter, por ejemplo, '|', debe estar entre comillas dobles "|" para no lanzar una conversión inválida const char.

#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

De manera poco habitual, estoy usando CentOS actualmente, por lo que mi versión del compilador está a continuación. La versión C ++ (g ++), C ++ 98 predeterminada:

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.
oOpSgEo
fuente
0

Si está dispuesto a usar std::strings, puede usar la función de esta aplicación de muestra strsubtal cual o actualizarla si desea que tome un tipo diferente o un conjunto de parámetros para lograr aproximadamente el mismo objetivo. Básicamente, utiliza las propiedades y funcionalidades de std::stringpara borrar rápidamente el conjunto de caracteres coincidentes e insertar los caracteres deseados directamente dentro del std::string. Cada vez que realiza esta operación de reemplazo, el desplazamiento se actualiza si aún puede encontrar caracteres coincidentes para reemplazar, y si no puede debido a nada más para reemplazar, devuelve la cadena en su estado desde la última actualización.

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

Si no desea confiar en usar std::strings como sus parámetros para poder pasar cadenas de estilo C en su lugar, puede ver la muestra actualizada a continuación:

#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;
}
kayleeFrye_onDeck
fuente
0

Para situaciones simples, esto funciona bastante bien sin usar ninguna otra biblioteca que luego std :: string (que ya está en uso).

Reemplace todas las ocurrencias del carácter a con el carácter b en alguna_cadena :

for (size_t i = 0; i < some_string.size(); ++i) {
    if (some_string[i] == 'a') {
        some_string.replace(i, 1, "b");
    }
}

Si la cadena es grande o varias llamadas para reemplazar es un problema, puede aplicar la técnica mencionada en esta respuesta: https://stackoverflow.com/a/29752943/3622300

Guney Ozsan
fuente
0

Aquí hay una solución que hice, en un espíritu DRI máximo. buscará sNeedle en sHaystack y lo reemplazará por sReplace, nTimes si no es 0, de lo contrario, todas las ocurrencias de sNeedle. no buscará nuevamente en el texto reemplazado.

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;
}
no revelado
fuente