108

How do I replace all occurrences of a substring with another string, for std::strings?

std::string s ("One hello, two hellos.");
s = s.replace("hello", "world");  // something like this
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Adam Tegen
  • 25,378
  • 33
  • 125
  • 153
  • Possible duplicate of [Replace part of a string with another string](https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string) – VLL Aug 03 '18 at 07:25

10 Answers10

167

Using boost::replace_all:

#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
ilyaigpetrov
  • 3,657
  • 3
  • 30
  • 46
  • 1
    Note that you don't have to explicitly create std::string's for the pattern and replacement: boost::replace_all(target, "foo", "bar"); – Alexis Wilke Sep 10 '11 at 23:55
  • 4
    +1, with a caveat: `replace_all` will segfault for versions of boost > 1.43 on Sun Studio for any version < 12.3 – Brian Vandenberg Aug 23 '12 at 21:44
  • See [this answer](https://stackoverflow.com/a/5878802/365102) for a fast **non-boost** implementation of `replace_all`. – Mateen Ulhaq Jan 04 '22 at 13:29
79

Why not implement your own replace?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}
jotik
  • 17,044
  • 13
  • 58
  • 123
yves Baumes
  • 8,836
  • 7
  • 45
  • 74
  • 4
    You are messing a bit with memory here with all the calls to "replace" : complexity would be n² if you remove "o" from "ooooooo...o". I guess one can do better, but this solution has the merit of being easy to understand. – Zonko Sep 21 '11 at 08:57
  • 1
    Why is this not an actual for loop, rather than an obfuscated for loop? – Shirik Aug 21 '12 at 18:44
  • I am used to apply the 'least surprise' principle. For loops are for simple index increment use, most of time. Here, according to me, a while loop is clearer. – yves Baumes Aug 23 '12 at 19:16
  • 1
    @aldo As a general rule it is better to avoid complexity and, for instance, use regex as mentioned in other replies. But depending on your need you may want to control your project dependencies. A little code snippet that does what exactly you need, no more, is sometimes better. – yves Baumes Oct 27 '12 at 10:57
  • This is `O(n^2)`. For `O(n)`, one should avoid using `replace` repeatedly. See [this implementation](https://stackoverflow.com/a/70586898/365102), for example. – Mateen Ulhaq Jan 05 '22 at 01:42
44

In C++11, you can do this as a one-liner with a call to regex_replace:

#include <string>
#include <regex>

using std::string;

string do_replace( string const & in, string const & from, string const & to )
{
  return std::regex_replace( in, std::regex(from), to );
}

string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;

output:

Removeallspaces
Zaid
  • 678
  • 6
  • 13
Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • Notice also that `from` can be a regular expression -- so you can use more sophisticated matching criteria if you need to. What I don't see, is how to do this *without* applying some form of regular expression parsing -- instead using only a direct interpretation of the `from` characters. – Brent Bradburn Jun 23 '16 at 14:36
  • @nobar, yeah, if I remember properly the regex support in 4.8.x was not complete. Also you can have more sophisticated searches, but you get penalized time wise... It's going to be slower than the other more straight forward search and replace functions. – Alexis Wilke Aug 07 '16 at 05:17
  • 5
    Please note that this will work only for very basic alphanumeric characters and nothing else without doing a lot of preprocessing depending on the type of string. I haven't found a general purpose regex based string replace yet. – Piyush Soni Oct 21 '16 at 09:05
  • 1
    I think that it makes sense to note that it's probably going to be much slower than simple string manipulation such as `boost::replace_all`. – Dev Null Feb 26 '19 at 23:26
19

Why not return a modified string?

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
  • 1
    Your inplace version actually doing unnecessary work if there are more than one string to replace. Imagine replacing long string by a short one. Replace will have to shift rest of the string on every found entry. That is both bad for cache and unnecessary. – mixture May 13 '21 at 09:50
6

My templatized inline in-place find-and-replace:

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

It returns a count of the number of items substituted (for use if you want to successively run this, etc). To use it:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");
Marius
  • 3,372
  • 1
  • 30
  • 36
  • 5
    I tried this sample under GCC but it wouldn't compile - it didn't like the use of T::size_t. Replacing T::size_t with typename T::size_type fixes the problem. – Andrew Wyatt Jul 12 '11 at 13:29
4

Performant O(n) replace all

Many other answers repeatedly call std::string::replace, which requires the string to be overwritten repeatedly, which results in poor performance. In contrast, this uses a std::string buffer so that each character of the string is only traversed once:

void replace_all(
    std::string& s,
    std::string const& toReplace,
    std::string const& replaceWith
) {
    std::string buf;
    std::size_t pos = 0;
    std::size_t prevPos;

    // Reserves rough estimate of final size of string.
    buf.reserve(s.size());

    while (true) {
        prevPos = pos;
        pos = s.find(toReplace, pos);
        if (pos == std::string::npos)
            break;
        buf.append(s, prevPos, pos - prevPos);
        buf += replaceWith;
        pos += toReplace.size();
    }

    buf.append(s, prevPos, s.size() - prevPos);
    s.swap(buf);
}

Usage:

replace_all(s, "text to replace", "new text");

Full example:

#include <iostream>

void replace_all(
    std::string& s,
    std::string const& toReplace,
    std::string const& replaceWith
) {
    std::string buf;
    std::size_t pos = 0;
    std::size_t prevPos;

    // Reserves rough estimate of final size of string.
    buf.reserve(s.size());

    while (true) {
        prevPos = pos;
        pos = s.find(toReplace, pos);
        if (pos == std::string::npos)
            break;
        buf.append(s, prevPos, pos - prevPos);
        buf += replaceWith;
        pos += toReplace.size();
    }

    buf.append(s, prevPos, s.size() - prevPos);
    s.swap(buf);
}

int main() {
    std::string s("hello hello, mademoiselle!");
    replace_all(s, "hello", "bye");
    std::cout << s << std::endl;
}

Output:

bye bye, mademoiselle!

Note: Previous versions of this answer used std::ostringstream, which has some overhead. The latest version uses std::string::append instead, as recommended by @LouisGo.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
  • Note that even it's O(n) while using `stringstream`, it might be still comparably slow than string operation while the size of string is ~600 characters. Refer to my [naive profiling](https://godbolt.org/z/3e7evEeMo); – Louis Go Aug 16 '22 at 02:08
  • Note that even it's O(n) while using stringstream, it might be still comparably slow than string operation while the size of string is ~600 characters. [benchmark](https://godbolt.org/z/zecYcMhvM). I'll suggest to use `std::string::append` – Louis Go Aug 21 '22 at 12:47
3

The easiest way (offering something near what you wrote) is to use Boost.Regex, specifically regex_replace.

std::string has built in find() and replace() methods, but they are more cumbersome to work with as they require dealing with indices and string lengths.

Alan
  • 45,915
  • 17
  • 113
  • 134
  • 3
    There are also the boost string algorithms, including replace_all (regex might be a bit heavy-weight for such simple substitution). – UncleBens Sep 29 '09 at 20:13
3

I believe this would work. It takes const char*'s as a parameter.

//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
   //ASSERT(find != NULL);
   //ASSERT(replace != NULL);
   size_t findLen = strlen(find);
   size_t replaceLen = strlen(replace);
   size_t pos = 0;

   //search for the next occurrence of find within source
   while ((pos = source.find(find, pos)) != std::string::npos)
   {
      //replace the found string with the replacement
      source.replace( pos, findLen, replace );

      //the next line keeps you from searching your replace string, 
      //so your could replace "hello" with "hello world" 
      //and not have it blow chunks.
      pos += replaceLen; 
   }
}
Daniel Ryan
  • 6,976
  • 5
  • 45
  • 62
Adam Tegen
  • 25,378
  • 33
  • 125
  • 153
  • Given that `size_type` for a string is `unsigned`, your `>=` check in the loop condition will always be `true`. You have to use `std::string::npos` there. – Pavel Minaev Sep 29 '09 at 19:28
  • size_type is not unsigned. It's unsigned on many platforms, but not all. – Alan Sep 29 '09 at 19:32
  • 16
    Why in the world is this not part of std::string? Is there any other serious String class in the world of programming that does not offer a 'find and replace' operation? Surely it's more common than having two iterators and wanting to replace the text between them?? Sometimes std::string feels like a car with a tunable spectrum windshield but no way to roll down the driver's window. – Spike0xff Nov 02 '09 at 17:08
  • 1
    @Spike0xff boost has `roll_down_window` – ta.speot.is Sep 07 '12 at 01:28
  • @Alan, why are you saying that, when it's not true? According to the spec, `std::string::size_type` is always unsigned (and is usually `size_t`), where as `std::string::difference_type` is always signed (and usually `ptrdiff_t`). FYI: http://en.cppreference.com/w/cpp/string/basic_string – gustaf r Jan 06 '13 at 12:08
  • 1
    @gustafr: My mistake. I've worked on systems where older compilers defined size_t improperly. – Alan Jan 06 '13 at 17:33
  • @Spike0xff: "Why is this not part of `std::string`"? It's consistent with the [STL](https://en.wikipedia.org/wiki/Standard_Template_Library) philosophy that "STL algorithms are independent of containers, which significantly reduces the complexity of the library.". In other words, you could create a more general find/replace algorithm that works for `std::string`, but also works for other containers. `std::regex_replace` (C++11) is just such an algorithm. – Brent Bradburn Jun 23 '16 at 14:54
3
#include <string>

using std::string;

void myReplace(string& str,
               const string& oldStr,
               const string& newStr) {
  if (oldStr.empty()) {
    return;
  }

  for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
    str.replace(pos, oldStr.length(), newStr);
    pos += newStr.length();
  }
}

The check for oldStr being empty is important. If for whatever reason that parameter is empty you will get stuck in an infinite loop.

But yeah use the tried and tested C++11 or Boost solution if you can.

ericcurtin
  • 1,499
  • 17
  • 20
1
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates 
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr, 
    const std::string& replacer)
{
    // Prevent an infinite loop if the input is empty
    if (searchStr == "") {
        return str;
    }

    std::string result = "";
    size_t pos = 0;
    size_t pos2 = str.find(searchStr, pos);

    while (pos2 != std::string::npos) {
        result += str.substr(pos, pos2-pos) + replacer;
        pos = pos2 + searchStr.length();
        pos2 = str.find(searchStr, pos);
    }

    result += str.substr(pos, str.length()-pos);
    return result;
}
  • 1
    We only need to search for new matches from the last match, that's why the algorithm carefully tracks the last match in pos. pos2 always stores the next match, so we concatenate the string between pos and pos2 to the result, then advance pos and pos2. If no other match can be found, we concatenate the remainder of the string to result. – Björn Ganster Sep 21 '17 at 08:23