Remplacer la sous-chaîne par une autre sous-chaîne C ++

90

Comment pourrais-je remplacer une sous-chaîne dans une chaîne par une autre sous-chaîne en C ++, quelles fonctions puis-je utiliser?

eg: string test = "abc def abc def";
test.replace("abc", "hij").replace("def", "klm"); //replace occurrence of abc and def with other substring
Steveng
la source
5
À peu près un double de stackoverflow.com/questions/3418231/… qui a une solution plus robuste dans la réponse acceptée.
dave-holm

Réponses:

74

Il n'y a pas une fonction intégrée en C ++ pour ce faire. Si vous souhaitez remplacer toutes les instances d'une sous-chaîne par une autre, vous pouvez le faire en mélangeant les appels à string::findet string::replace. Par exemple:

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

Dans la dernière ligne de ce code, j'ai incrémenté indexde la longueur de la chaîne qui a été insérée dans la chaîne. Dans cet exemple particulier - remplacer "abc"par "def"- ce n'est pas réellement nécessaire. Cependant, dans un cadre plus général, il est important d'ignorer la chaîne qui vient d'être remplacée. Par exemple, si vous souhaitez remplacer "abc"par "abcabc", sans sauter le segment de chaîne nouvellement remplacé, ce code remplacerait continuellement des parties des chaînes nouvellement remplacées jusqu'à ce que la mémoire soit épuisée. Indépendamment, il peut être légèrement plus rapide de sauter de toute façon ces nouveaux personnages, car cela permet d'économiser du temps et des efforts pour la string::findfonction.

J'espère que cela t'aides!

templatetypedef
la source
6
Je ne pense pas que vous deviez incrémenter l'index parce que vous avez déjà remplacé les données afin qu'elles ne les récupèrent pas de toute façon.
rossb83
1
@Aidiakapi Si cela est transformé en une fonction à usage général, il ne restera pas coincé dans une boucle infinie car il fait avancer la position de recherche ( index) au-delà de la partie de la chaîne qui a été remplacée.
Tim R.
1
@TimR. Vous avez raison, je répondais à rossb83 qui déclare que l'augmentation de l'index n'est pas nécessaire. J'essayais juste d'éviter la désinformation. Donc pour tout le monde: augmenter l'index de la longueur de la chaîne remplacée (dans ce cas 3) est nécessaire . Ne le supprimez pas de l'exemple de code.
Aidiakapi
@FrozenKiwi Je suis surpris d'entendre cela. Etes-vous sûr que c'est le cas?
templatetypedef
1
@JulianCienfuegos Je viens de mettre à jour la réponse pour résoudre ce problème - merci de l'avoir signalé! (De plus, Aidiakapi est quelqu'un d'autre ... je ne sais pas qui c'est.)
templatetypedef
68

Boost String Algorithms Library manière:

#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"
  );
}
Oleg Svechkarenko
la source
4
Geai. J'ai besoin d'un boost pour remplacer toutes les sous-chaînes.
Johannes Overmann
2
Boost est surtout exagéré.
Konrad
62

Dans , vous pouvez utiliser std::regex_replace:

#include <string>
#include <regex>

std::string test = "abc def abc def";
test = std::regex_replace(test, std::regex("def"), "klm");
Jingguo Yao
la source
4
Ce serait génial si nous avions C ++ 11 !!
Michele
1
#include <regex>
Stepan Yakovenko
42

Je pense que toutes les solutions échoueront si la longueur de la chaîne de remplacement est différente de la longueur de la chaîne à remplacer. (recherchez "abc" et remplacez-le par "xxxxxx") Une approche générale pourrait être:

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 );
    }
}
rotmax
la source
40
str.replace(str.find(str2),str2.length(),str3);

  • str est la chaîne de base
  • str2 est la sous-chaîne à trouver
  • str3 est la sous-chaîne de remplacement
Jeff Zacher
la source
3
Cela ne remplace que la première occurrence, n'est-ce pas?
jpo38
4
Je suggère de s'assurer que le résultat de str.find (str2) n'est pas égal à std :: string :: npos auto found = str.find (str2); if (trouvé! = std :: string :: npos) str.replace (trouvé, str2.length (), str3);
Geoff Lentsch
1
Je n'avais pas l'intention d'écrire toute l'application avec ceci, mais sans aucun contrôle sur l'entrée, il y a des cas de cela qui ne sont pas définis ....
Jeff Zacher
19

Le remplacement des sous-chaînes ne devrait pas être si difficile.

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 vous avez besoin de performances, voici une fonction optimisée qui modifie la chaîne d'entrée, elle ne crée pas de copie de la chaîne:

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

Tests:

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;

Production:

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
la source
besoin d'ajouter une vérification if (search.empty()) { return; }pour éviter une boucle infinie lorsque vous passez une recherche vide
Programmeur iOS
J'ai essayé la fonction ReplaceString - ne fonctionne pas. Mais répondez ci-dessous: str.replace (str.find (str2), str2.length (), str3); juste simple et fonctionne 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;
}

Comme ce n'est pas un membre de la stringclasse, cela ne permet pas une syntaxe aussi agréable que dans votre exemple, mais ce qui suit fera l'équivalent:

test = string_replace( string_replace( test, "abc", "hij"), "def", "klm")
Michael Burr
la source
2

Généralisant sur la réponse de rotmax, voici une solution complète pour rechercher et remplacer toutes les instances d'une chaîne. Si les deux sous-chaînes sont de taille différente, la sous-chaîne est remplacée en utilisant string :: erase et string :: insert., Sinon la chaîne la plus rapide :: replace est utilisée.

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 );
    }
  }
}
Neoh
la source
2

Si vous êtes sûr que la sous-chaîne requise est présente dans la chaîne, cela remplacera la première occurrence de "abc"to"hij"

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

Il plantera si vous n'avez pas "abc" dans le test, alors utilisez-le avec précaution.

ch0kee
la source
1

Voici une solution que j'ai écrite en utilisant la tactique du constructeur:

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

Tests:

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

}
Den-Jason
la source
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;
    }

Je pense que cela correspond à vos besoins avec peu de code!

Alessio
la source
Vous ne prenez pas en considération les occurrences / remplacements multiples
Elias Bachaalany
0

la version améliorée par @Czarek Tomczak.
permettre à la fois std::stringet 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();
    }
}
programmeur iOS
la source
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;
};
Krecker
la source
0

Voici une solution utilisant la récursivité qui remplace toutes les occurrences d'une sous-chaîne par une autre sous-chaîne. Cela fonctionne quelle que soit la taille des chaînes.

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

Exemple d'utilisation:

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;
brotalnie
la source
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;
}

Solution courte où vous n'avez pas besoin de bibliothèques supplémentaires.

Altinsystems
la source
Il y a 14 autres réponses à cette question. Pourquoi ne pas expliquer pourquoi le vôtre est meilleur?
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;
}
Alex
la source