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. –
Raid const
and if I wrote a utility method like this I would only call it if I knew the replacement were valid –
Cioffred const
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.) –
Raid size_t start_pos = string.find(from);
should be: size_t start_pos = str.find(from);
–
Danaus from
is an empty string –
Cioffred replace_all
: en.cppreference.com/w/cpp/string/basic_string/replace –
Fenian std::string
. –
Progressist std::string_view
for from
and to
? –
Cartierbresson 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. –
Jana string
with string.toStdString()
. –
Gualtiero String
to std::string
, because the question is not related to Qt. Please consider doing that - I'll gladly upvote your answer afterwards. –
Jana R"(\$name)"
instead of "\\$name"
. –
Telecast Using std::string::replace
:
s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");
"$name"
in s
string. –
Iatrochemistry 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");
–
Searby 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.
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 modification –
Scolex 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");
–
Allies 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);
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. –
Chancery 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(), "\\", "/"), ":", "");
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);
}
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;
}
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
I realize this thread is old as dirt now, but I'm just building off of Michael Mrozek's accepted answer for the replaceAll()
function.
Why not 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");
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. –
Allies © 2022 - 2024 — McMap. All rights reserved.