146

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
1

21 Answers 21

130

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"
Sign up to request clarification or add additional context in comments.

5 Comments

That would be great if we had c++11!!
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") ...
@ThomasVincent can you explain this in more details, maybe in an answer block; not just a comment?
@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 ...
@ThomasVincent std::regex("\\.json") to be precise
106

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.

3 Comments

My bad, I read the wrong doc (std::replace, not str replace). Too much haste
@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.
@templatetypedef thanks! Sorry to haunt you six years after you wrote an answer.
80

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

3 Comments

Jay. I need boost to replace all substrings.
Boost is mostly an overkill.
I find it amusing that an enormous portion of answers to C++ questions have a simple, concise answer using boost, and every single one of them has a "boost is overkill" comment attached. I can't imagine using C++ without boost...
58
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

1 Comment

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

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

Comments

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

2 Comments

need add check if (search.empty()) { return; } to avoid infinite loop when pass empty 'search'.
Tried ReplaceString function - not worked. But answer bellow: str.replace(str.find(str2),str2.length(),str3); just simple and works well.
12
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.

2 Comments

There are 14 other answers to this question. Why not offer an explanation as to why yours is any better?
Looks like the most elegant answer thus far, without any overengineering
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")

Comments

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

1 Comment

Thanks. Used without problem for some years; eventually needed to make both oldString and newString into const parameters, however.
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.

Comments

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

}

Comments

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

Comments

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!

1 Comment

You are not taking into consideration multiple occurences / replacements
1

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;

Comments

1

This seems to work well for me. It is indeed necessary to increment index as otherwise you can get in an endless loop when your from and to strings are similar.

#include <iostream>
#include <string>

void replace_all(std::string& str, const std::string& from, const std::string& to){
  size_t index = 0;
  while (true) {
    // find substring
    index = str.find(from, index);
    if (index == std::string::npos) break;

    // relace it with target to
    str.replace(index, from.length(), to);

    // jump further to after replacement
    index += to.length(); 
  }
}

int main(){
  std::string test = "cool hello hello world";
  replace_all(test, "cool ", "C");
  replace_all(test, "hello ", "+");
  replace_all(test, "++", "++ ");
  std::cout << "'" << test << "'" << std::endl;

  // world is doubled 3 times. so we get it *8
  std::string test2 = "Hello world";
  replace_all(test2, "world", "world world!");
  replace_all(test2, "world", "world world");
  replace_all(test2, "world", "world world");
  std::cout << "'" << test2 << "'" << std::endl;
}

Output:

'C++ world'
'Hello world world world world world world world world!'

Comments

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

Comments

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

Comments

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;

Comments

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

Comments

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

Comments

0

Just another implementation with mix of tchar and std.

#include <string>
#include <tchar.h>

namespace std {
    using tstring = std::basic_string<TCHAR, std::char_traits<TCHAR>, allocator<TCHAR> >;
}

namespace my
{
    inline std::tstring replace_strings(std::tstring str, const std::tstring & from, std::tstring to)
    {
        size_t start_pos = 0;

        while ((start_pos = str.find(from, start_pos)) != std::tstring::npos) {
            str.replace(start_pos, from.length(), to);
            start_pos += to.length();
        }

        return str;
    }

    template <typename Functor>
    inline std::tstring replace_strings(std::tstring str, const std::tstring & from, Functor && to_functor)
    {
        size_t index = 0;
        size_t start_pos = 0;

        while ((start_pos = str.find(from, start_pos)) != std::tstring::npos) {
            auto to = std::forward<Functor>(to_functor)(index, start_pos);
            str.replace(start_pos, from.length(), to);
            start_pos += to.length();
            index++;
        }

        return str;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.