138

How could I replace a substring in a string with another substring in C++, what functions could I use?

eg: string test = "abc def abc def";
test.replace("abc", "hij").replace("def", "klm"); //replace occurrence of abc and def with other substring
Mark Cidade
  • 98,437
  • 31
  • 224
  • 236
Steveng
  • 2,385
  • 6
  • 23
  • 20
  • 7
    Pretty much a duplicate of http://stackoverflow.com/questions/3418231/c-replace-part-of-a-string-with-another-string which has a more robust solution in the accepted answer. – dave-holm Jun 21 '11 at 04:44

19 Answers19

117

In , you can use std::regex_replace:

#include <string>
#include <regex>

std::string test = "abc def abc def";
test = std::regex_replace(test, std::regex("def"), "klm"); // replace 'def' -> 'klm'
// test = "abc klm abc klm"
Gelldur
  • 11,187
  • 7
  • 57
  • 68
Jingguo Yao
  • 7,320
  • 6
  • 50
  • 63
  • 4
    That would be great if we had c++11!! – Michele Mar 27 '17 at 17:34
  • 4
    be careful this do no generalize well, you might end up passing something `std:regex` interpret differently like `std::regex_replace(test, std::regex("."), "klm")` ... – Thomas Vincent Feb 02 '22 at 20:56
  • @ThomasVincent can you explain this in more details, maybe in an answer block; not just a comment? – Amit Jun 21 '22 at 13:54
  • 3
    @Amit I don't know if it deserves an answer block. All I am saying is that for the simple `"def"` example it works but if what you what to replace "means" something in the regex syntax you will not have the expected result! For example, if you want to replace `".json"` you will have to use `std::regex("\.json")` otherwise (almost) any sub-string endind with `json` will also be replace. This is because `.` mean (almost) any character in regex ... – Thomas Vincent Aug 25 '22 at 19:26
  • 1
    @ThomasVincent `std::regex("\\.json")` to be precise – Danvil Nov 18 '22 at 00:56
101

There is no one built-in function in C++ to do this. If you'd like to replace all instances of one substring with another, you can do so by intermixing calls to string::find and string::replace. For example:

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

In the last line of this code, I've incremented index by the length of the string that's been inserted into the string. In this particular example - replacing "abc" with "def" - this is not actually necessary. However, in a more general setting, it is important to skip over the string that's just been replaced. For example, if you want to replace "abc" with "abcabc", without skipping over the newly-replaced string segment, this code would continuously replace parts of the newly-replaced strings until memory was exhausted. Independently, it might be slightly faster to skip past those new characters anyway, since doing so saves some time and effort by the string::find function.

starball
  • 20,030
  • 7
  • 43
  • 238
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 9
    I don't believe you would need to increment index because you have already replaced the data so it wouldn't pick it up anyway. – rossb83 Nov 04 '12 at 17:25
  • 1
    @Aidiakapi If this is turned into a general purpose function, it won't get stuck in an infinite loop because it advances the search position (`index`) past the part of the string that was replaced. – Tim R. Aug 14 '15 at 22:50
  • 3
    @TimR. You're right, I was responding to rossb83 who states that the increment of the index is unnecessary. Was just trying to prevent misinformation. So for everyone else: **Increasing the index by the length of the replaced string** (in this case 3) **is necessary**. Do not remove it from the code sample. – Aidiakapi Aug 14 '15 at 23:56
  • @FrozenKiwi I'm surprised to hear that. Are you sure that's the case? – templatetypedef Mar 16 '16 at 17:03
  • My bad, I read the wrong doc (std::replace, not str replace). Too much haste – FrozenKiwi Mar 16 '16 at 19:41
  • 1
    @rossb83 The comments need to be cleaned up or expounded upon. There is one comment with 5 upvotes saying you don't have to increment the index and then one person saying you need to in bold. This is not helpful to someone coming here to learn. –  Mar 21 '17 at 18:38
  • @rossb83 + templatetypedef(Who I think used to be Aidiakapi) I can write tests to see what happens and read the documentation and after I do that and come here to respond, then there will be a 2 to 1 vote which is still confusing for others. –  Mar 21 '17 at 18:50
  • 1
    @JulianCienfuegos I just updated the answer to address this - thanks for pointing that out! (Also, Aidiakapi is someone else... not sure who that is.) – templatetypedef Mar 21 '17 at 18:52
  • @templatetypedef thanks! Sorry to haunt you six years after you wrote an answer. –  Mar 21 '17 at 19:12
79

Boost String Algorithms Library way:

#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
  • 2,508
  • 25
  • 30
54
str.replace(str.find(str2),str2.length(),str3);

Where

  • str is the base string
  • str2 is the sub string to find
  • str3 is the replacement substring
DIF
  • 2,470
  • 6
  • 35
  • 49
Jeff Zacher
  • 751
  • 6
  • 7
  • 4
    This only replaces first occurence, doesn't it? – jpo38 Aug 10 '17 at 09:01
  • 10
    I would suggest ensuring the result of str.find(str2) does not equal std::string::npos auto found = str.find(str2); if(found != std::string::npos) str.replace(found, str2.length(), str3); – Geoff Lentsch Nov 30 '17 at 20:09
  • 1
    I wasn't intending to write the entire application with this, but without any checks on the input there are cases of this that are undefined.... – Jeff Zacher Mar 29 '19 at 09:10
51

I think all solutions will fail if the length of the replacing string is different from the length of the string to be replaced. (search for "abc" and replace by "xxxxxx") A general approach might be:

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
  • 511
  • 4
  • 2
23

Replacing substrings should not be that hard.

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

If you need performance, here is an optimized function that modifies the input string, it does not create a copy of the string:

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;

Output:

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
  • 20,079
  • 5
  • 49
  • 56
  • need add check `if (search.empty()) { return; }` to avoid infinite loop when pass empty 'search'. – iOS-programmer May 14 '15 at 12:14
  • Tried ReplaceString function - not worked. But answer bellow: str.replace(str.find(str2),str2.length(),str3); just simple and works well. – KAMIKAZE Jul 26 '17 at 11:27
9
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;
}

Short solution where you don't need any extra Libraries.

Altinsystems
  • 186
  • 2
  • 8
  • 1
    There are 14 other answers to this question. Why not offer an explanation as to why yours is any better? – chb Jul 04 '19 at 17:16
  • 3
    Looks like the most elegant answer thus far, without any overengineering – Ichthyo Aug 15 '21 at 16:03
7
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;
}

Since it's not a member of the string class, it doesn't allow quite as nice a syntax as in your example, but the following will do the equivalent:

test = string_replace( string_replace( test, "abc", "hij"), "def", "klm")
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
4

Generalizing on rotmax's answer, here is a full solution to search & replace all instances in a string. If both substrings are of different size, the substring is replaced using string::erase and string::insert., otherwise the faster string::replace is used.

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
  • 15,906
  • 14
  • 66
  • 78
  • Thanks. Used without problem for some years; eventually needed to make both `oldString` and `newString` into `const` parameters, however. – Ron Burk May 25 '21 at 03:12
4

If you are sure that the required substring is present in the string, then this will replace the first occurence of "abc" to "hij"

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

It will crash if you dont have "abc" in test, so use it with care.

Utsav T
  • 1,515
  • 2
  • 24
  • 42
ch0kee
  • 736
  • 4
  • 12
3

Here is a solution I wrote using the builder tactic:

#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
  • 2,395
  • 1
  • 22
  • 17
2
#include <string>

First:

void replace_first(std::string& text, const std::string& from,
   const std::string& to)
{
    const auto at = text.find(from, 0);

    if (at != std::string::npos)
        text.replace(at, from.length(), to);
}

All:

void replace_all(std::string& text, const std::string& from,
   const std::string& to)
{
    for (auto at = text.find(from, 0); at != std::string::npos;
        at = text.find(from, at + to.length()))
    {
        text.replace(at, from.length(), to);
    }
}

Count:

size_t replace_count(std::string& text,
   const std::string& from, const std::string& to)
{
    size_t count = 0;

    for (auto at = text.find(from, 0); at != std::string::npos;
        at = text.find(from, at + to.length()))
    {
        ++count;
        text.replace(at, from.length(), to);
    }

    return count;
}

Copy:

std::string replace_all_copy(const std::string& text,
   const std::string& from, const std::string& to)
{
    auto copy = text;
    replace_all(copy, from, to);
    return copy;
}
evoskuil
  • 1,011
  • 1
  • 7
  • 13
1
    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;
    }

I think this fits your requirement with few code!

Alessio
  • 19
  • 1
0

the impoved version by @Czarek Tomczak.
allow both std::string and 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();
    }
}
iOS-programmer
  • 1,411
  • 2
  • 10
  • 13
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
  • 21
  • 3
0

Here is a solution using recursion that replaces all occurrences of a substring with another substring. This works no matter the size of the strings.

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

Usage example:

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;
brotalnia
  • 1
  • 1
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
  • 1
  • 1
0

I think this the shortest solution. it will replace all def to abc.

 string test = "abc def abc def";
 regex p("def");
 cout<<regex_replace(test, p, "abc")<<endl;
Tofazzal haque
  • 544
  • 4
  • 10
0
std::string replaceSubstring(std::string str, const std::string& from, const std::string& to) {
    size_t index = 0;
    while (true) {
        index = str.find(from, index);
        if (index == std::string::npos) {
            break;
        }
        str.replace(index, from.length(), to);
        index += to.length();
    }
    return str;
}
chubercik
  • 534
  • 1
  • 5
  • 13