Replace substring with another substring C++
Asked Answered
N

21

141

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
Natal answered 10/1, 2011 at 3:42 Comment(1)
Pretty much a duplicate of #3418731 which has a more robust solution in the accepted answer.Toughminded
S
121

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"
Stricken answered 23/12, 2016 at 1:49 Comment(5)
That would be great if we had c++11!!Iglesias
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") ...Aegisthus
@ThomasVincent can you explain this in more details, maybe in an answer block; not just a comment?Residual
@Residual 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 ...Aegisthus
@ThomasVincent std::regex("\\.json") to be preciseDosi
T
103

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.

Teamwork answered 10/1, 2011 at 3:45 Comment(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.Munson
@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.Zurheide
@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.Courtenay
@FrozenKiwi I'm surprised to hear that. Are you sure that's the case?Teamwork
My bad, I read the wrong doc (std::replace, not str replace). Too much hasteRepurchase
@Munson 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.Endor
@Munson + 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.Endor
@JulianCienfuegos I just updated the answer to address this - thanks for pointing that out! (Also, Aidiakapi is someone else... not sure who that is.)Teamwork
@Teamwork thanks! Sorry to haunt you six years after you wrote an answer.Endor
S
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"
  );
}
Sexagesimal answered 13/11, 2012 at 14:26 Comment(3)
Jay. I need boost to replace all substrings.Weinshienk
Boost is mostly an overkill.Tourniquet
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...Fallfish
F
55
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
Floor answered 19/8, 2014 at 17:0 Comment(3)
This only replaces first occurence, doesn't it?Zabrze
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);Keesee
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....Floor
C
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 );
    }
}
Crichton answered 12/3, 2013 at 21:41 Comment(0)
D
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
Dashed answered 4/2, 2013 at 0:23 Comment(2)
need add check if (search.empty()) { return; } to avoid infinite loop when pass empty 'search'.Mcmasters
Tried ReplaceString function - not worked. But answer bellow: str.replace(str.find(str2),str2.length(),str3); just simple and works well.Micturition
K
11
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.

Keir answered 4/7, 2019 at 16:42 Comment(2)
There are 14 other answers to this question. Why not offer an explanation as to why yours is any better?Remnant
Looks like the most elegant answer thus far, without any overengineeringHousewarming
B
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")
Bossuet answered 10/1, 2011 at 6:21 Comment(0)
C
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.

Cephalalgia answered 10/1, 2011 at 3:59 Comment(0)
A
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 );
    }
  }
}
Aranyaka answered 8/1, 2014 at 6:41 Comment(1)
Thanks. Used without problem for some years; eventually needed to make both oldString and newString into const parameters, however.Makowski
A
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);

}
Anterior answered 1/8, 2018 at 10:15 Comment(0)
J
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;
}
Johnniejohnny answered 16/5, 2021 at 12:12 Comment(0)
P
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!

Psephology answered 18/8, 2011 at 13:10 Comment(1)
You are not taking into consideration multiple occurences / replacementsInfusorian
M
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();
    }
}
Mcmasters answered 14/5, 2015 at 12:19 Comment(0)
W
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;
};
Waal answered 23/3, 2017 at 7:47 Comment(0)
T
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;
Trammel answered 12/6, 2018 at 13:28 Comment(0)
B
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;
}
Barger answered 1/5, 2020 at 18:19 Comment(0)
F
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;
Fluorosis answered 1/9, 2022 at 8:34 Comment(0)
P
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;
}
Potty answered 5/6, 2023 at 15:3 Comment(0)
Q
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;
    }
}
Quar answered 28/9, 2023 at 16:42 Comment(0)
K
0

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!'
Katzir answered 25/12, 2023 at 18:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.