Como substituir todas as ocorrências de um caractere na string?

480

Qual é a maneira eficaz de substituir todas as ocorrências de um personagem por outro std::string?

big-z
fonte

Respostas:

742

std::stringnão contém essa função, mas você pode usar a replacefunção autônoma do algorithmcabeçalho.

#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
fonte
6
std::stringé um contêiner projetado especificamente para operar com sequências de caracteres. link
Kirill V. Lyadvinsky
164
Infelizmente, isso permite substituir apenas um caracter por outro. Ele não pode substituir um caractere por mais caracteres (ou seja, por uma string). Existe uma maneira de substituir uma pesquisa por mais caracteres?
SasQ
6
@Kirill V. Lyadvinsky E se eu quiser apenas remover uma ocorrência.
SIFE
4
@ KirillV.Lyadvinsky: Quando eu uso esse método para substituir todos os x por y, o resultado é uma string y longa, independentemente da string original. Estou curioso para saber qual seria o problema. (o código é exatamente o mesmo que você escreveu)
Transcendent
6
@ Transcendente: Isso é exatamente o que acontece ao std::string::replace()invés de std::replace()! 'x' ( char) é implicitamente convertido para size_t[valor 120], portanto, toda a cadeia de caracteres ou parte dela será preenchida com 120 cópias de 'y'.
19Byue
127

Também pensei em lançar a solução 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, "#", "@");
UncleZeiv
fonte
Faltam alguns -Isinalizadores para o seu compilador para que ele encontre as bibliotecas do Boost no seu sistema. Talvez você precise instalá-lo primeiro.
Martin Ueding
O exemplo acima é mais eficaz, pois é lançado com o std lib.No all using boost library ;-)
hfrmobile
122

A questão está centrada na charactersubstituição, mas, como achei esta página muito útil (especialmente a observação de Konrad ), gostaria de compartilhar essa implementação mais generalizada, que também permite lidar com 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;

Saídas:

Number_Of_Beans

XXjXugtXty

hhjhugthty


EDITAR:

O exposto acima pode ser implementado de uma maneira mais adequada, caso as performances sejam de sua preocupação, retornando nothing ( void) e realizando as alterações diretamente na string strfornecida como argumento, passada por endereço em vez de por valor . Isso evitaria cópias inúteis e caras da string original, enquanto retornava o resultado. Sua ligação, então ...

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 isso seja útil para alguns outros ...

Gauthier Boaglio
fonte
4
Este tem um problema de desempenho nos casos em que a string de origem é grande e há muitas ocorrências da string a serem substituídas. string :: replace () seria chamado muitas vezes, o que causa muitas cópias de string. Veja minha solução que resolve esse problema.
Minastaros
1
Nit picking à frente: por endereço => por referência . Se é um endereço ou não, é um detalhe de implementação.
Max Truxa
1
Você deve realmente verificar se a fromstring está vazia, caso contrário, um loop sem fim ocorrerá.
nov
34

Imagine um blob binário grande em que todos os 0x00 bytes sejam substituídos por "\ 1 \ x30" e todos os 0x01 bytes por "\ 1 \ x31" porque o protocolo de transporte não permite \ 0-bytes.

Nos casos em que:

  • a cadeia de substituição e a cadeia a ser substituída têm comprimentos diferentes,
  • existem muitas ocorrências da cadeia a ser substituída na cadeia de origem e
  • a cadeia de origem é grande,

as soluções fornecidas não podem ser aplicadas (porque substituem apenas caracteres únicos) ou têm um problema de desempenho, porque chamariam string :: replace várias vezes, o que gera cópias do tamanho do blob repetidamente. (Eu não sei a solução de reforço, talvez esteja tudo bem nessa perspectiva)

Este percorre todas as ocorrências na string de origem e constrói a nova string peça por peça uma 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
fonte
Essa é, de longe, a melhor solução aqui, construída apenas no STL. Se você quiser inserir uma função personalizada para facilitar o uso em qualquer lugar, faça a seguinte.
Roger Sanders
21

Uma simples localização e substituição de um único caractere seria algo como:

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

Para fazer isso para toda a cadeia, a coisa mais fácil a fazer seria fazer um loop até você s.findcomeçar a retornar npos. Suponho que você também pode pegar range_errorpara sair do loop, mas isso é meio feio.

TED
fonte
7
Embora essa seja provavelmente uma solução adequada quando o número de caracteres a serem substituídos for pequeno comparado ao comprimento da string, ela não é dimensionada corretamente. À medida que a proporção de caracteres na string original que precisa ser substituída aumenta, esse método se aproximará de O (N ^ 2) com o tempo.
ee
7
Verdade. Minha filosofia geral é fazer a coisa fácil (escrever e ler) até que as ineficiências estejam causando problemas reais. Há algumas circunstâncias em que você pode ter cordas hediondas onde O (N ** 2) importa, mas 99% das vezes minhas cordas são 1 K ou menos.
TED
3
... dito isso, eu gosto mais do método de Kirill (e já o havia votado).
TED
O que acontece se "x" não for encontrado? Além disso, por que você está usando chaves duplas?
Prasath Govind
@PrasathGovind - eu estava apenas mostrando as chamadas necessárias (daí "algo parecido"). Detalhes importantes, mas obscuros, como manipulação adequada de erros, foram deixados como um exercício para o leitor. Quanto às "chaves duplas", não sei ao certo o que são ou o que você está falando. Para mim, uma "chave" é o {personagem. Não sei o que é uma "chave dupla". Talvez você tenha algum tipo de problema de fonte?
TED
6

Se você estiver procurando substituir mais de um caractere e estiver lidando apenas com std::string, então esse trecho funcionaria, substituindo sNeedle no sHaystack por sReplace, e sNeedle e sReplace não precisam ter o mesmo tamanho. Essa rotina usa o loop while para substituir todas as ocorrências, e não apenas a primeira encontrada da esquerda para a direita.

while(sHaystack.find(sNeedle) != std::string::npos) {
  sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}
Volomike
fonte
Este é O (n ^). Você poderia fazer isso em O (n) tempo.
Changming Sun
3
@ChangmingSun, qual solução O (n) você quer dizer?
Habakuk
2
Isso fará um loop infinito se o kNeedle for uma subcadeia de sReplace.
prideout 11/01
Além disso, há uma findligação duas vezes. Considere tornar esse resultado uma variável temporária.
Luc Bloom
4

Como Kirill sugeriu, use o método de substituição ou itere ao longo da cadeia substituindo cada caractere independentemente.

Como alternativa, você pode usar o findmétodo ou find_first_ofdependendo do que você precisa fazer. Nenhuma dessas soluções fará o trabalho de uma só vez, mas com algumas linhas extras de código você deve fazê-las funcionar para você. :-)

Konrad
fonte
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
fonte
Se wordfor longo, pode haver muita sobrecarga ao chamar a função. Você pode otimizar isso passando word, targete replacementcomo referências const.
precisa saber é o seguinte
2

E o Abseil StrReplaceAll ? No arquivo de cabeçalho:

// 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
fonte
1

Moda antiga :-)

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 \ áudio \ youtube \ libre \ falta \

Iván Rodríguez
fonte
0

Isso funciona! Usei algo semelhante a isso para um aplicativo de livraria, onde o inventário foi armazenado em um CSV (como um arquivo .dat). Porém, no caso de um único caractere, o que significa que o substituto é apenas um único caractere, por exemplo, '|', ele deve estar entre aspas duplas "|" para não lançar um constante constante de conversão inválido.

#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

Infelizmente, estou usando o CentOS atualmente, então minha versão do compilador está abaixo. A versão C ++ (g ++), C ++ 98 padrão:

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
fonte
0

Se você estiver disposto a usar std::strings, poderá usar a strsubfunção desse aplicativo de amostra como está ou atualizá-la se desejar que ele use um tipo ou conjunto de parâmetros diferente para atingir aproximadamente o mesmo objetivo. Basicamente, ele usa as propriedades e funcionalidades de std::stringpara apagar rapidamente o conjunto de caracteres correspondente e inserir os caracteres desejados diretamente no std::string. Toda vez que faz essa operação de substituição, o deslocamento é atualizado se ainda puder encontrar caracteres correspondentes para substituir e, se não puder mais devido a substituição, ele retornará a string em seu estado desde a última atualização.

#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 você não quiser usar std::strings como parâmetros para passar as seqüências de estilo C, poderá ver o exemplo atualizado abaixo:

#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
fonte
0

Para situações simples, isso funciona muito bem sem o uso de qualquer outra biblioteca e, em seguida, std :: string (que já está em uso).

Substitua todas as ocorrências do caractere a pelo caractere b em some_string :

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

Se a sequência for grande ou várias chamadas a serem substituídas forem um problema, você poderá aplicar a técnica mencionada nesta resposta: https://stackoverflow.com/a/29752943/3622300

Guney Ozsan
fonte
0

aqui está uma solução que eu criei, no espírito máximo de DRI. ele pesquisará sNeedle no sHaystack e o substituirá por sReplace, nTimes se não for 0, caso contrário, todas as ocorrências de sNeedle. não procurará novamente no texto substituído.

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;
}
não revelado
fonte