262

How do I replace part of a string with another string using the standard C++ libraries?

QString s("hello $name");  // Example using Qt.
s.replace("$name", "Somename");
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Tom Leese
  • 19,309
  • 12
  • 45
  • 70
  • 1
    There is a std tag on the question, but perhaps you might be interested in boost's string algorithms, which also includes a wide choice of replace algorithms (inplace/copy, case sensitive/case insensitive, replace first/last/all/n-th). – UncleBens Aug 05 '10 at 22:25
  • 3
    possible duplicate of [How do I Search/Find and Replace in a standard string?](http://stackoverflow.com/questions/1494399/how-do-i-search-find-and-replace-in-a-standard-string) – Jet Jun 23 '15 at 19:59

18 Answers18

374

There's a function to find a substring within a string (find), and a function to replace a particular range in a string with another string (replace), so you can combine those to get the effect you want:

bool replace(std::string& str, const std::string& from, const std::string& to) {
    size_t start_pos = str.find(from);
    if(start_pos == std::string::npos)
        return false;
    str.replace(start_pos, from.length(), to);
    return true;
}

std::string string("hello $name");
replace(string, "$name", "Somename");

In response to a comment, I think replaceAll would probably look something like this:

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if(from.empty())
        return;
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
    }
}
Michael Mrozek
  • 169,610
  • 28
  • 168
  • 175
  • 3
    How would I fix it if the original string had more that one instance of "$name" and I wanted to replace all of them. – Tom Leese Aug 05 '10 at 19:14
  • 3
    Why aren't `from` and `to` passed per `const` reference? What does your function if `from` isn't there? `-1` from me for that. – sbi Aug 05 '10 at 19:15
  • Add a loop and use the "pos" argument of the find() method – S.C. Madsen Aug 05 '10 at 19:17
  • 15
    @sbi Fixed, although you could've phrased it as recommendations instead of attacks -- it simply didn't occur to me, I rarely think to use `const` and if I wrote a utility method like this I would only call it if I knew the replacement were valid – Michael Mrozek Aug 05 '10 at 19:19
  • 14
    @Michael: Good, I turned my down-vote into an up-vote. Dismissing `const` is disregarding one of C++' best tools. [Passing per `const` reference should be the default mode for function parameters.](http://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c/2139254#2139254) (FTR, without the `const`, you couldn't even pass string literals to your function, because you cannot bind temporaries to non-`const` references. So the function wouldn't even do what it was written to.) – sbi Aug 05 '10 at 21:13
  • After copying and using the replaceAll method, my string kept getting shorter with each call. According to http://www.cplusplus.com/reference/string/string/replace/ the signature for str.replace is this: string& replace ( size_t pos1, size_t n1, const string& str ); Usually an argument named n1 indicates a length and changing the end_pos argument to just be the length of the from string solved the problem. – dave-holm Jun 21 '11 at 07:03
  • Line: `size_t start_pos = string.find(from);` should be: `size_t start_pos = str.find(from);` – xnx Jan 17 '12 at 20:33
  • This method goes to endless cycle if from=="" – boqapt Jul 31 '12 at 12:24
  • @user484936 Did you try it? I assume you mean the second one, but neither of them gets stuck in a loop if `from` is an empty string – Michael Mrozek Jul 31 '12 at 13:35
  • @MichaelMrozek Yes, I tried second one and got infinite cycle. "to" must be also empty string – boqapt Jul 31 '12 at 14:06
  • 75
    Is this still the only solution in 2018? If so and any C++ committee are reading this, sort it out. It's embarrassing. split(string, string) and replace(string, string) please! – user997112 Jul 31 '18 at 12:53
  • Please consider if replace string with empty str "", your answer will segmentation fault!! – Nicholas Jela May 17 '22 at 15:32
  • In the example code for std::replace, there is an implementation of `replace_all`: https://en.cppreference.com/w/cpp/string/basic_string/replace – thd Jan 04 '23 at 10:11
  • The old MFC [CString](https://learn.microsoft.com/en-us/previous-versions/5bzxfsea(v=vs.140)#members) class has had string replacement functionality for at least one decade. A shame that there is still no such thing in `std::string`. – Jabberwocky Apr 03 '23 at 10:11
  • Maybe update this answer to use `std::string_view` for `from` and `to`? – user2672165 Jun 18 '23 at 13:05
164

With C++11 you can use std::regex like so:

#include <regex>
...
std::string string("hello $name");
string = std::regex_replace(string, std::regex("\\$name"), "Somename");

The double backslash is required for escaping an escape character.

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
Tom
  • 2,481
  • 1
  • 15
  • 16
  • I'm pretty sure `std::regex_replace` doesn't accept Qt's string. – BartoszKP Jul 23 '15 at 19:06
  • 2
    You are right. As it happens QString provides a replace method which accepts a QRexExp, allowing to use Qt's own stuff. But I think The current answer could be corrected by replacing `string` with `string.toStdString()`. – Tom Jul 24 '15 at 22:36
  • 1
    Or just by changing `String` to `std::string`, because the question is not related to Qt. Please consider doing that - I'll gladly upvote your answer afterwards. – BartoszKP Jul 25 '15 at 10:07
  • 7
    Raw string allows to write `R"(\$name)"` instead of `"\\$name"`. – Jarod42 Jul 22 '16 at 12:30
  • 10
    How much will this be slower than find/replace without considering the constructing time of std::regex? – jw_ Dec 29 '19 at 12:14
  • Does this replace all occurrances? Or just the first? – Owl Apr 26 '22 at 23:11
39

Using std::string::replace:

s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
S.C. Madsen
  • 5,100
  • 5
  • 32
  • 50
  • 3
    From what I can see the std::string replace method doesn't take two strings as I would like. – Tom Leese Aug 05 '10 at 19:10
  • 5
    This doesnt work for me. sizeof should be replaced by string("Somename").size()-1 – TimZaman Apr 10 '14 at 08:34
  • @TimZaman: That puzzles me, the documentation clearly states you can initialize from a C-style string. – S.C. Madsen Apr 10 '14 at 18:26
  • 6
    the 2nd argument should be the length of "$name" (instead of the length of "Somename"), shouldn't it? – Daniel Kiss Jan 13 '16 at 19:56
  • 3
    This wouldn't work if there are multiple occurrences of `"$name"` in `s` string. – bimbi May 31 '21 at 03:10
  • this answer is straight up wrong with no explanation of what is going on. – Sam B Jan 10 '23 at 02:06
  • Not sure I follow what you mean @SamB? I assure you the answer works for the example given in the question. But admittedly not for multiple occurrences of "$name". But I think that is quite a different story than being "straight up wrong". – S.C. Madsen Jan 27 '23 at 14:12
13

To have the new string returned use this:

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 modified: " 
          << 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
  • Your call to subject.replace in ReplaceStringInPlace() is that really modifying the string inplace? – Damian Jul 13 '16 at 09:08
  • I briefly looked at the source and it looks like it does use move semantics to move the front of the old string into place, so that is not copied, but the new piece inserted is copied into the old string and the tail of the old string is copied into the resized buffered of the old string. It is possible that the string expands so much the entire underlying buffer is reallocated, but if you replace 1 to 1 as in his example, I think it does occur "in place", or without any copying, but if you expand the string, only the first part of the old string is not copied, and only maybe then. – Motomotes Oct 13 '16 at 21:38
9
string.replace(string.find("%s"), string("%s").size(), "Something");

You could wrap this in a function but this one-line solution sounds acceptable. The problem is that this will change the first occurence only, you might want to loop over it, but it also allows you to insert several variables into this string with the same token (%s).

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
maxoumime
  • 3,441
  • 1
  • 17
  • 23
  • 4
    Like the style but found the different strings confusing ^^ `str.replace(str.find("%s"), string("%s").size(), "Something");` – Paul Würtz Apr 23 '17 at 14:18
6

I use generally this:

std::string& replace(std::string& s, const std::string& from, const std::string& to)
{
    if(!from.empty())
        for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size())
            s.replace(pos, from.size(), to);
    return s;
}

It repeatedly calls std::string::find() to locate other occurrences of the searched for string until std::string::find() doesn't find anything. Because std::string::find() returns the position of the match we don't have the problem of invalidating iterators.

Galik
  • 47,303
  • 4
  • 80
  • 117
6

Yes, you can do it, but you have to find the position of the first string with string's find() member, and then replace with it's replace() member.

string s("hello $name");
size_type pos = s.find( "$name" );
if ( pos != string::npos ) {
   s.replace( pos, 5, "somename" );   // 5 = length( $name )
}

If you are planning on using the Standard Library, you should really get hold of a copy of the book The C++ Standard Library which covers all this stuff very well.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
4

If all strings are std::string, you'll find strange problems with the cutoff of characters if using sizeof() because it's meant for C strings, not C++ strings. The fix is to use the .size() class method of std::string.

sHaystack.replace(sHaystack.find(sNeedle), sNeedle.size(), sReplace);

That replaces sHaystack inline -- no need to do an = assignment back on that.

Example usage:

std::string sHaystack = "This is %XXX% test.";
std::string sNeedle = "%XXX%";
std::string sReplace = "my special";
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
std::cout << sHaystack << std::endl;
Volomike
  • 23,743
  • 21
  • 113
  • 209
4

This could be even better to use

void replace(string& input, const string& from, const string& to)
{
    auto pos = 0;
    while(true)
    {
        size_t startPosition = input.find(from, pos);
        if(startPosition == string::npos)
            return;
        input.replace(startPosition, from.length(), to);
        pos += to.length();
    }
}
Yashwanth Kumar
  • 341
  • 3
  • 6
  • 2
    `string s = "ha"; replace(s, "h", "uhoh");` – Jonathan Wakely Aug 12 '20 at 02:34
  • instead of making a snide comment @JonathanWakely you could actually explicitly describe the problem you are implying in your comment – v2v1 Oct 19 '21 at 22:10
  • @v2v1 It's not snide, and I wasn't implying, I was demonstrating. Teach a man to fish, and all that. Working out why the function doesn't work is a useful exercise. If you want there to be a comment explaining it, *you* can add one. I note you didn't do that either, you'd rather just criticise my comment. – Jonathan Wakely Oct 20 '21 at 12:10
  • @JonathanWakely using "uhoh" and "ha" is snide to me. And sure, "demonstrate" and force the person reading this to run an infinite loop? Why not just tell them outright? It's not like they're going to gain anything by blowing up the code themselves. And the explanation is quite simple, so you might as well have put that instead. – v2v1 Oct 20 '21 at 16:00
4

What about the boost solution:

boost::replace_all(value, "token1", "token2");
camp0
  • 372
  • 1
  • 5
3
wstring myString = L"Hello $$ this is an example. By $$.";
wstring search = L"$$";
wstring replace = L"Tom";
for (int i = myString.find(search); i >= 0; i = myString.find(search))
    myString.replace(i, search.size(), replace);
user3016543
  • 149
  • 1
  • 4
2

If you want to do it quickly you can use a two scan approach. Pseudo code:

  1. first parse. find how many matching chars.
  2. expand the length of the string.
  3. second parse. Start from the end of the string when we get a match we replace, else we just copy the chars from the first string.

I am not sure if this can be optimized to an in-place algo.

And a C++11 code example but I only search for one char.

#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

void ReplaceString(string& subject, char search, const string& replace)
{   
    size_t initSize = subject.size();
    int count = 0;
    for (auto c : subject) { 
        if (c == search) ++count;
    }

    size_t idx = subject.size()-1 + count * replace.size()-1;
    subject.resize(idx + 1, '\0');

    string reverseReplace{ replace };
    reverse(reverseReplace.begin(), reverseReplace.end());  

    char *end_ptr = &subject[initSize - 1];
    while (end_ptr >= &subject[0])
    {
        if (*end_ptr == search) {
            for (auto c : reverseReplace) {
                subject[idx - 1] = c;
                --idx;              
            }           
        }
        else {
            subject[idx - 1] = *end_ptr;
            --idx;
        }
        --end_ptr;
    }
}

int main()
{
    string s{ "Mr John Smith" };
    ReplaceString(s, ' ', "%20");
    cout << s << "\n";

}
Damian
  • 4,395
  • 4
  • 39
  • 67
1
std::string replace(std::string base, const std::string from, const std::string to) {
    std::string SecureCopy = base;

    for (size_t start_pos = SecureCopy.find(from); start_pos != std::string::npos; start_pos = SecureCopy.find(from,start_pos))
    {
        SecureCopy.replace(start_pos, from.length(), to);
    }

    return SecureCopy;
}
  • 3
    Can you please explain this code (in your answer)? You might get more upvotes that way! – The Guy with The Hat Apr 17 '14 at 15:04
  • Inputs where the replacement string has the old string in it like `,` replaced with `,,` will cause an infinite loop. Can fix by offseting the position in replace with the length difference between from and to. – Daemon Feb 13 '23 at 20:31
1

My own implementation, taking into account that string needs to be resized only once, then replace can happen.

template <typename T>
std::basic_string<T> replaceAll(const std::basic_string<T>& s, const T* from, const T* to)
{
    auto length = std::char_traits<T>::length;
    size_t toLen = length(to), fromLen = length(from), delta = toLen - fromLen;
    bool pass = false;
    std::string ns = s;

    size_t newLen = ns.length();

    for (bool estimate : { true, false })
    {
        size_t pos = 0;

        for (; (pos = ns.find(from, pos)) != std::string::npos; pos++)
        {
            if (estimate)
            {
                newLen += delta;
                pos += fromLen;
            }
            else
            {
                ns.replace(pos, fromLen, to);
                pos += delta;
            }
        }

        if (estimate)
            ns.resize(newLen);
    }

    return ns;
}

Usage could be for example like this:

std::string dirSuite = replaceAll(replaceAll(relPath.parent_path().u8string(), "\\", "/"), ":", "");
TarmoPikaro
  • 4,723
  • 2
  • 50
  • 62
1

It requires some case analysis to write an optimal (or at least not quadratic) algorithm for all inputs.

The naive algorithm (also the most up-voted answer at the time of writing) is quadratic in the worst case because it shifts the whole suffix at each iteration, so it's O(n) calls to replace(), O(n) each because of that shift.

Essentially, the haystack string can be seen as a sequence of strings that are equal to what, separated by some other strings (that don't have what as a substring). So, all we need to do to avoid quadratic runtime is to make sure that we copy each of such strings only once, not the whole suffix or prefix each time. It can be achieved with the "two pointer technique", the exact way we do that depends on who is longer:

  • if the replacements will shrink the string (that is, with is shorter than what), then let's start from the beginning of the string and maintain two offsets — read and write one — and the write one will never be greater. After traversing the whole string (in just one pass, in-place), the write offset stands for the last character we've copied, so it's also the new size of the string.
  • if the replacements will grow the string (with is longer than what), we'll do the similar thing but backwards. To know which write offset to begin with, we're going to have to know the number of occurrences and resize the string in advance, otherwise it's pretty symmetric to the previous case.
  • if with and what have equal length, we don't have to shift the string, so pretty much any approach will suffice — the first one looks better because it only requires one pass.
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <string>
#include <string_view>

size_t CountOccurrences(std::string_view s, std::string_view needle) {
    size_t res = 0;
    size_t pos = 0;
    while ((pos = s.find(needle, pos)) != std::string_view::npos) {
        ++res;
        pos += needle.size();
    }
    return res;
}

std::string ReplaceNotLonger(std::string s, std::string_view what, std::string_view with) {
    assert(what.size() >= with.size());
    std::string_view::size_type wpos = 0;
    std::string_view::size_type rpos = 0;
    while (true) {
        auto new_rpos = s.find(what, rpos);
        if (new_rpos == std::string::npos) {
            new_rpos = s.size();
        }
        auto n = new_rpos - rpos;
        std::copy(s.begin() + rpos, s.begin() + new_rpos, s.begin() + wpos);
        wpos += n;
        rpos = new_rpos;
        if (rpos == s.size()) {
            break;
        }
        std::copy(with.begin(), with.end(), s.begin() + wpos);
        wpos += with.size();
        rpos += what.size();
    }
    s.resize(wpos);
    return s;
}

std::string ReplaceLonger(std::string s, std::string_view what, std::string_view with) {
    assert(what.size() < with.size());
    auto occurrences = CountOccurrences(s, what);
    auto rpos = s.size();
    auto wpos = rpos + occurrences * (with.size() - what.size());
    s.resize(wpos);
    
    while (wpos != rpos) {
        auto new_rpos = s.rfind(what, rpos - what.size());
        if (new_rpos == std::string::npos) {
            new_rpos = 0;
        } else {
            new_rpos += what.size();
        }
        auto n = rpos - new_rpos;
        std::copy_backward(s.begin() + new_rpos, s.begin() + rpos, s.begin() + wpos);
        wpos -= n;
        rpos = new_rpos;
        if (wpos == rpos) {
            break;
        }
        std::copy_backward(with.begin(), with.end(), s.begin() + wpos);
        wpos -= with.size();
        rpos -= what.size();
    }
    return s;
}

std::string Replace(std::string s, std::string_view what, std::string_view with) {
    assert(!what.empty());
    if (what.size() >= with.size()) {
        return ReplaceNotLonger(std::move(s), what, with);
    }
    return ReplaceLonger(std::move(s), what, with);
}
Ignat Loskutov
  • 781
  • 4
  • 9
0

I'm just now learning C++, but editing some of the code previously posted, I'd probably use something like this. This gives you the flexibility to replace 1 or multiple instances, and also lets you specify the start point.

using namespace std;

// returns number of replacements made in string
long strReplace(string& str, const string& from, const string& to, size_t start = 0, long count = -1) {
    if (from.empty()) return 0;

    size_t startpos = str.find(from, start);
    long replaceCount = 0;

    while (startpos != string::npos){
        str.replace(startpos, from.length(), to);
        startpos += to.length();
        replaceCount++;

        if (count > 0 && replaceCount >= count) break;
        startpos = str.find(from, startpos);
    }

    return replaceCount;
}
someprogrammer
  • 229
  • 2
  • 13
0

Here is a one liner that uses c++'s standard library.

The replacement better not have the old string in it (ex: replacing , with ,,), otherwise you have an INFINITE LOOP. Moreso, it is slow for large strings compared to other techniques because the find operations start at the begining of the string call every time. Look for better solutions if you're not too lazy. I put this in for completeness and inspiration for others. You've been warned.

while(s.find(old_s) != string::npos) s.replace(s.find(old_s), old_s.size(), new_s);

And a lambda option

auto replaceAll = [](string& s, string o, string n){ while(s.find(o) != string::npos) s.replace(s.find(o), o.size(), n); };

// EXAMPLES:
// Used like
string text = "hello hello world";
replaceAll(text, "hello", "bye"); // Changes text to "bye bye world"
// Do NOT use like
string text = "hello hello world";
replaceAll(text, "hello", "hello hello"); // Loops forever
Daemon
  • 51
  • 4
-2

You can use this code for remove subtring and also replace , and also remove extra white space . code :

#include<bits/stdc++.h>
using namespace std;

void removeSpaces(string &str)
{   
    int n = str.length();
    int i = 0, j = -1;

    bool spaceFound = false;
    while (++j <= n && str[j] == ' ');

    while (j <= n)
    {
        if (str[j] != ' ')
        {
          
            if ((str[j] == '.' || str[j] == ',' ||
                 str[j] == '?') && i - 1 >= 0 &&
                 str[i - 1] == ' ')
                str[i - 1] = str[j++];
            else str[i++] = str[j++];
 
            spaceFound = false;
        }
        else if (str[j++] == ' ')
        {
            if (!spaceFound)
            {
                str[i++] = ' ';
                spaceFound = true;
            }
        }
    }

    if (i <= 1)
         str.erase(str.begin() + i, str.end());
    else str.erase(str.begin() + i - 1, str.end());
}
int main()
{
    string s;
    cin >> s;

    for(int i = s.find("WUB"); i >= 0; i = s.find("WUB"))
        s.replace(i,3," ");
    removeSpaces(s);
    cout << s << endl;

    return 0;
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
mubasshir00
  • 307
  • 3
  • 9
  • This replaces `"WUB"` with `" "` but that wasn't what OP asked for, and neither was removing spaces (which what most of your answer is doing). Your find/replace solution doesn't work if the replacement also matches the string to be replaced. – Jonathan Wakely Oct 20 '21 at 12:15
  • No one is asking you to be an overachiever, just answer the question as is, you don't get extra points for complicating your code solving problems they didn't ask for. – AustinWBryan Nov 20 '22 at 03:35
  • Like the question is "How to remove part of a string with another," and you respond with a function named "RemovedSpaces," which is overly complicated for the simple task, and by the way this would remove spaces that are wanted. So "This is a text with WUB in it" turns into "Thisisatextwithinit". – AustinWBryan Nov 20 '22 at 03:39