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'
}
}
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.
Using std::string::replace:
s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");
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).
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.
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();
}
}
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:
first parse. find how many matching chars.
expand the length of the string.
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";
}
What about the boost solution:
boost::replace_all(value, "token1", "token2");
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;
}
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(), "\\", "/"), ":", "");
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
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;
}
Related
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'
}
}
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.
Using std::string::replace:
s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");
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).
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.
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();
}
}
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:
first parse. find how many matching chars.
expand the length of the string.
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";
}
What about the boost solution:
boost::replace_all(value, "token1", "token2");
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;
}
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(), "\\", "/"), ":", "");
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
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;
}
I want to replace a substring in a string with something that depends on the substring between to delimiters. Little example:
I got the string
The result is __--__3__--__.
and a function
int square(int x): { return x*x };
Now I want to output just the string with the result without delimiters, so:
The result is 9.
I already tried several algorithms but none of them worked yet.
Best regard
My best attempt to far:
const std::string emptyString = "";
std::string ExtractString(std::string source, std::string start, std::string end)
{
std::size_t startIndex = source.find(start);
// If the starting delimiter is not found on the string
// stop the process, you're done!
//
if (startIndex == std::string::npos)
{
return emptyString;
}
// Adding the length of the delimiter to our starting index
// this will move us to the beginning of our sub-string.
//
startIndex += start.length();
// Looking for the end delimiter
//
std::string::size_type endIndex = source.find(end, startIndex);
// Returning the substring between the start index and
// the end index. If the endindex is invalid then the
// returned value is empty string.
return source.substr(startIndex, endIndex - startIndex);
}
int square(int x): { return x*x };
int main() {
std::string str = "The result is __--__3__--__.";
std::string foundNum = ExtractString(str, "__--__", "__--__");
int foundNumInt = atoi(foundNum.c_str());
int result = square(foundNumInt);
std::string toReplace = "__--__";
toReplace.append(foundNumInt);
toReplace.append("__--__");
str.replace(str.begin(), str.end(), toReplace, result);
}
The Question is: How to take the first string given ( The result is __--__<number>__--__.>, get the number from it, preform a function on that number, and then end with a string that looks like this The result is <number squared>.
Here is a way to take the first string, find the number. I then just squared the number, but you could plug that into your own function of you wanted to.
std::string s = "The result is __--__3__--__.";
std::regex r( "[0-9]+");
std::smatch m;
//
std::sregex_iterator iter(s.begin(), s.end(), r);
std::sregex_iterator end;
std::string value;
//
int index = 0;
while (iter != end)
{
for (unsigned i = 0; i < iter->size(); ++i)
{
value = (*iter)[i];
}
++iter;
index++;
}
int num = stoi(value);
int answer = num*num;
s = s.substr(0, s.find('_'));
s = s + " " + std::to_string(answer);
std::cout << s << std::endl;
Have you tried std::string::find?
const std::string example_data = "The result is __--__3__--__.";
static const char text_to_find[] = "__--__";
const std::string::size_type start_position = example_data.find(text_to_find);
if (start_position != std::string::npos)
{
const std::string::size_type replacement_start_position = start_position + sizeof(text_to_find) - 1;
if (replacement_start_position < example_data.length())
{
// Perform replacement
}
}
The "sizeof(text_to_find) - 1" returns the length of the text, without counting the terminating nul character.
To skip past the number, you could do something like:
const std::string after_number_position = example_data.find(replacement_start_position, "_");
The substring between replacement_start_position and after_number_position will contain your number. You can use a variety of functions to convert the substring to a number.
See also std::ostringstream for converting numbers to text.
Edit 1:
Corrected declaration of replacement_start_position.
You must need these functions(for c++17, much faster):
auto replace_all
(std::string str, std::string_view from, std::string_view to) noexcept -> decltype(str) {
unsigned 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();
}
return str;
}
auto remove_all
(std::string str, std::string_view from) noexcept -> decltype(str) {
return replace_all(str, from, "");
}
and for later versions:
std::string replace_all
(std::string str, std::string from, std::string to) noexcept {
unsigned 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();
}
return str;
}
std::string remove_all
(std::string str, std::string from) noexcept {
return replace_all(str, from, "");
}
I tested:
int main() {
std::string str = "__+__hello__+__";
std::cout << remove_all(str, "__+__");
std::cin.get();
return 0;
}
and my output was:
hello
If s is a std::string, then is there a function like the following?
s.replace("text to replace", "new text");
Replace first match
Use a combination of std::string::find and std::string::replace.
Find the first match:
std::string s;
std::string toReplace("text to replace");
size_t pos = s.find(toReplace);
Replace the first match:
s.replace(pos, toReplace.length(), "new text");
A simple function for your convenience:
void replace_first(
std::string& s,
std::string const& toReplace,
std::string const& replaceWith
) {
std::size_t pos = s.find(toReplace);
if (pos == std::string::npos) return;
s.replace(pos, toReplace.length(), replaceWith);
}
Usage:
replace_first(s, "text to replace", "new text");
Demo.
Replace all matches
Define this O(n) method using std::string as a buffer:
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");
Demo.
Boost
Alternatively, use boost::algorithm::replace_all:
#include <boost/algorithm/string.hpp>
using boost::replace_all;
Usage:
replace_all(s, "text to replace", "new text");
Do we really need a Boost library for seemingly such a simple task?
To replace all occurences of a substring use this function:
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
Yes: replace_all is one of the boost string algorithms:
Although it's not a standard library, it has a few things on the standard library:
More natural notation based on ranges rather than iterator pairs. This is nice because you can nest string manipulations (e.g., replace_all nested inside a trim). That's a bit more involved for the standard library functions.
Completeness. This isn't hard to be 'better' at; the standard library is fairly spartan. For example, the boost string algorithms give you explicit control over how string manipulations are performed (i.e., in place or through a copy).
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str("one three two four");
string str2("three");
str.replace(str.find(str2),str2.length(),"five");
cout << str << endl;
return 0;
}
Output
one five two four
like some say boost::replace_all
here a dummy example:
#include <boost/algorithm/string/replace.hpp>
std::string path("file.gz");
boost::replace_all(path, ".gz", ".zip");
Not exactly that, but std::string has many replace overloaded functions.
Go through this link to see explanation of each, with examples as to how they're used.
Also, there are several versions of string::find functions (listed below) which you can use in conjunction with string::replace.
find
rfind
find_first_of
find_last_of
find_first_not_of
find_last_not_of
Also, note that there are several versions of replace functions available from <algorithm> which you can also use (instead of string::replace):
replace
replace_if
replace_copy
replace_copy_if
// replaced text will be in buffer.
void Replace(char* buffer, const char* source, const char* oldStr, const char* newStr)
{
if(buffer==NULL || source == NULL || oldStr == NULL || newStr == NULL) return;
int slen = strlen(source);
int olen = strlen(oldStr);
int nlen = strlen(newStr);
if(olen>slen) return;
int ix=0;
for(int i=0;i<slen;i++)
{
if(oldStr[0] == source[i])
{
bool found = true;
for(int j=1;j<olen;j++)
{
if(source[i+j]!=oldStr[j])
{
found = false;
break;
}
}
if(found)
{
for(int j=0;j<nlen;j++)
buffer[ix++] = newStr[j];
i+=(olen-1);
}
else
{
buffer[ix++] = source[i];
}
}
else
{
buffer[ix++] = source[i];
}
}
}
Here's the version I ended up writing that replaces all instances of the target string in a given string. Works on any string type.
template <typename T, typename U>
T &replace (
T &str,
const U &from,
const U &to)
{
size_t pos;
size_t offset = 0;
const size_t increment = to.size();
while ((pos = str.find(from, offset)) != T::npos)
{
str.replace(pos, from.size(), to);
offset = pos + increment;
}
return str;
}
Example:
auto foo = "this is a test"s;
replace(foo, "is"s, "wis"s);
cout << foo;
Output:
thwis wis a test
Note that even if the search string appears in the replacement string, this works correctly.
void replace(char *str, char *strFnd, char *strRep)
{
for (int i = 0; i < strlen(str); i++)
{
int npos = -1, j, k;
if (str[i] == strFnd[0])
{
for (j = 1, k = i+1; j < strlen(strFnd); j++)
if (str[k++] != strFnd[j])
break;
npos = i;
}
if (npos != -1)
for (j = 0, k = npos; j < strlen(strRep); j++)
str[k++] = strRep[j];
}
}
int main()
{
char pst1[] = "There is a wrong message";
char pfnd[] = "wrong";
char prep[] = "right";
cout << "\nintial:" << pst1;
replace(pst1, pfnd, prep);
cout << "\nfinal : " << pst1;
return 0;
}
void replaceAll(std::string & data, const std::string &toSearch, const std::string &replaceStr)
{
// Get the first occurrence
size_t pos = data.find(toSearch);
// Repeat till end is reached
while( pos != std::string::npos)
{
// Replace this occurrence of Sub String
data.replace(pos, toSearch.size(), replaceStr);
// Get the next occurrence from the current position
pos =data.find(toSearch, pos + replaceStr.size());
}
}
More CPP utilities: https://github.com/Heyshubham/CPP-Utitlities/blob/master/src/MEString.cpp#L60
is there a function like the following?
One other(in addition to using boost and other methods given in different answers) possible way of doing this is using std::regex_replace as shown below:
std::string s{"my name is my name and not my name mysometext myto"}; //this is the original line
std::string replaceThis = "my";
std::string replaceWith = "your";
std::regex pattern("\\b" + replaceThis + "\\b");
std::string replacedLine = std::regex_replace(s, pattern, replaceWith);
std::cout<<replacedLine<<std::endl;
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
In c++11, 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"
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.
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"
);
}
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
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 );
}
}
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
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")
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.
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 );
}
}
}
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.
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);
}
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!
#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;
}
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();
}
}
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;
};
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;
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;
}
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;
I've spent the last hour and a half trying to figure out how to run a simple search and replace on a string object in C++.
I have three string objects.
string original, search_val, replace_val;
I want to run a search command on original for the search_val and replace all occurrences with replace_val.
NB: Answers in pure C++ only. The environment is XCode on the Mac OSX Leopard.
A loop should work with find and replace
void searchAndReplace(std::string& value, std::string const& search,std::string const& replace)
{
std::string::size_type next;
for(next = value.find(search); // Try and find the first match
next != std::string::npos; // next is npos if nothing was found
next = value.find(search,next) // search for the next match starting after
// the last match that was found.
)
{
// Inside the loop. So we found a match.
value.replace(next,search.length(),replace); // Do the replacement.
next += replace.length(); // Move to just after the replace
// This is the point were we start
// the next search from.
}
}
size_t start = 0;
while(1) {
size_t where = original.find(search_val, start);
if(where==npos) {
break;
}
original.replace(where, search_val.size(), replace_val);
start = where + replace_val.size();
}
For comparison here is the function in pure C:
http://www.pixelbeat.org/libs/string_replace.c
A little bit more elegant:
void searchAndReplace(std::string& value, std::string const& search,std::string const& replace) {
for(std::string::size_type idx = value.find(search);match
idx != std::string::npos;
next = value.find(search, idx + replace.size())
)
value.replace(next, search.size(), replace);
}
#include <boost/algorithm/string.hpp>
string newstring = boost::replace_all_copy(original, search_val, replace_val);
or, if you want in-place replacement
boost::replace_all(original, search_val, replace_val);
Simple...
But limited to replaceing single char only!!
#include <algorithm>
string foo = "abc.e";
std::replace(foo.begin(), foo.end(),'.','d');
result --> foo = "abcde";
This might result in a faster execution and preserves the original if wanted.
static std::string strreplace( const std::string &original, const std::string &pattern, const std::string &newtext ) {
std::stringstream ss;
std::string::size_type last = 0;
std::string::size_type it = original.find( pattern, last );
while( it != original.npos ) {
if( it-last > 0 ) {
ss << original.substr( last, it - last );
ss << newtext;
}
last = it + pattern.size( );
it = original.find( pattern, last );
}
return ss.str( );
}
This is probably your most concentrated version of string replace:
for ( string::size_type index = 0 ;
(index = value.find(from, index)) != string::npos ;
index += to.size() )
value.replace(index, from.size(), to);
A tested code with examples.
If you want the 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 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