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");
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");
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'
}
}
from and to passed per const reference? What does your function if from isn't there? -1 from me for that.const and if I wrote a utility method like this I would only call it if I knew the replacement were validconst is disregarding one of C++' best tools. Passing per const reference should be the default mode for function parameters. (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.)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.
std::regex_replace doesn't accept Qt's string.string with string.toStdString().R"(\$name)" instead of "\\$name".Using std::string::replace:
s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");
"$name" in s string.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
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).
str.replace(str.find("%s"), string("%s").size(), "Something");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.
What about the boost solution:
boost::replace_all(value, "token1", "token2");
boost::replace_all_copy() when a new std::string is preferred over modificationYes, 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.
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;
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();
}
}
string s = "ha"; replace(s, "h", "uhoh");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:
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.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.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);
}
If you want to do it quickly you can use a two scan approach. Pseudo code:
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";
}
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;
}
, replaced with ,, will cause an infinite loop. Can fix by offseting the position in replace with the length difference between from and to.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(), "\\", "/"), ":", "");
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
Building off of Michael Mrozek's accepted answer for the replaceAll() function, we could just do it like this:
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;
}
void replaceAll(string &str, const string &from, const string &to)
{
while(replace(str,from,to));
}
string hello = "Hello blargedy blargedy!";
replaceAll(hello, "blargedy","World");
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;
}
Several of the answers here reuse the original string for efficiency.
This will work if the replacement text is the same length as the text to find, or if the string is always short and the number of replacements is always low. However, since the algorithm needs to repeatedly copy the downstream contents for every "replace", it is unlikley to scale well.
Here is a version using stringstream that avoids the shuffle.
Both methods still suffer from the potential need to reallocate memory if the string grows.
#include <iostream>
#include <sstream>
#include <string_view>
#include <ranges>
std::string Replace(const std::string& text, const std::string& find, const std::string& replace)
{
std::stringstream result;
std::string_view textView(text);
std::string_view findView(find);
bool first = true;
for (const auto& p : (textView | std::views::split(findView)))
{
if (!first)
{
result << replace;
}
first = false;
result << std::string_view(p.begin(), p.end());
}
return result.str();
}
int main()
{
std::cerr << Replace("hello, world", "e", "E") << "\n";
std::cerr << Replace("zero one two three", "one", "ONE") << "\n";
std::cerr << Replace("one zero one two three one", "one", "ONE") << "\n";
}
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;
}
"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.