I need to search a string and edit the formatting of it.
So far I can replace the first occurrence of the string, but I am unable to do so with the next occurrences
/// Returns a version of 'str' where every occurrence of
/// 'find' is substituted by 'replace'.
/// - Inspired by James Kanze.
/// - http://stackoverflow.com/questions/20406744/
std::string replace_all(
const std::string & str , // where to work
const std::string & find , // substitute 'find'
const std::string & replace // by 'replace'
) {
using namespace std;
string result;
size_t find_len = find.size();
size_t pos,from=0;
while ( string::npos != ( pos=str.find(find,from) ) ) {
result.append( str, from, pos-from );
result.append( replace );
from = pos + find_len;
}
result.append( str, from , string::npos );
return result;
/*
This code might be an improvement to James Kanze's
because it uses std::string methods instead of
general algorithms [as 'std::search()'].
*/
}
int main() {
{
std::string test = "*A ... *A ... *A ...";
std::string changed = "*A\n ... *A\n ... *A\n ...";
assert( changed == replace_all( test, "*A", "*A\n" ) );
}
{
std::string GB = "My gorila ate the banana";
std::string gg = replace_all( GB, "gorila", "banana" );
assert( gg == "My banana ate the banana" );
gg = replace_all( gg, "banana", "gorila" );
assert( gg == "My gorila ate the gorila" );
std::string bb = replace_all( GB, "banana", "gorila" );
assert( gg == "My gorila ate the gorila" );
bb = replace_all( bb, "gorila" , "banana" );
assert( bb == "My banana ate the banana" );
}
{
std::string str, res;
str.assign( "ababaabcd" );
res = replace_all( str, "ab", "fg");
assert( res == "fgfgafgcd" );
str="aaaaaaaa"; assert( 8==str.size() );
res = replace_all( str, "aa", "a" );
assert( res == "aaaa" );
assert( "" == replace_all( str, "aa", "" ) );
str = "aaaaaaa"; assert( 7==str.size() );
res = replace_all( str, "aa", "a" );
assert( res == "aaaa" );
str = "..aaaaaa.."; assert( 10==str.size() );
res = replace_all( str, "aa", "a" );
assert( res == "..aaa.." );
str = "baaaac"; assert( 6==str.size() );
res = replace_all( str, "aa", "" );
assert( res == "bc" );
}
}
If ever the strings you need to invert are not of the same size:
void Replace::replace(std::string & str, std::string const & s1, std::string const & s2)
{
size_t pos;
pos = 0;
while ((pos = str.find(s1, pos)) != std::string::npos)
{
str.erase(pos, s1.length());
str.insert(pos, s2);
pos += s2.length();
}
return ;
}
The find
function takes an optional second argument: the position from which to begin searching. By default this is zero.
A good position to begin searching for the next match is the position where the previous replacement was inserted, plus that replacement's length. For instance if we insert a string of length 3 at position 7, then the next find
should begin at position 10.
If the search string happens to be a substring of the replacement, this approach will avoid an infinite loop. Imagine if you try to replace all occurrences of log
with analog
, but don't skip over the replacement.
Use std::regex_replace available with C++11. This does exactly what you want and more.
https://en.cppreference.com/w/cpp/regex/regex_replace
std::string const result = std::regex_replace( chartDataString, std::regex( "\\*A" ), "[A]\n" );
In case boost
is available, you can use the following:
std::string origStr = "this string has *A and then another *A";
std::string subStringToRemove = "*A";
std::string subStringToReplace = "[A]";
boost::replace_all(origStr , subStringToRemove , subStringToReplace);
To perform the modification on the original string, OR
std::string result = boost::replace_all_copy(origStr , subStringToRemove , subStringToReplace);
To perform the modifications without modifying the original string.
It's fairly awkward (and probably not too efficient) to do it in place. I usually use a function along the lines of:
std::string
replaceAll( std::string const& original, std::string const& from, std::string const& to )
{
std::string results;
std::string::const_iterator end = original.end();
std::string::const_iterator current = original.begin();
std::string::const_iterator next = std::search( current, end, from.begin(), from.end() );
while ( next != end ) {
results.append( current, next );
results.append( to );
current = next + from.size();
next = std::search( current, end, from.begin(), from.end() );
}
results.append( current, next );
return results;
}
Basically, you loop as long as you can find an instance of
from
, appending the intermediate text and to
, and advancing
to the next instance of from
. At the end, you append any text
after the last instance of from
.
(If you're going to do much programming in C++, it's probably
a good idea to get used to using iterators, like the above,
rather than the special member functions of std::string
.
Things like the above can be made to work with any of the C++
container types, and for this reason, are more idiomatic.)