44

I found this on another stack question:

//http://stackoverflow.com/questions/3418231/c-replace-part-of-a-string-with-another-string // void replaceAll(std::string& str, const std::string& from, const std::string& to) { size_t start_pos = 0; while((start_pos = str.find(from, start_pos)) != std::string::npos) { size_t end_pos = start_pos + from.length(); str.replace(start_pos, end_pos, to); start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' } } 

and my method:

string convert_FANN_array_to_binary(string fann_array) { string result = fann_array; cout << result << "\n"; replaceAll(result, "-1 ", "0"); cout << result << "\n"; replaceAll(result, "1 ", "1"); return result; } 

which, for this input:

cout << convert_FANN_array_to_binary("1 1 -1 -1 1 1 "); 

now, the output should be "110011"

here is the output of the method:

1 1 -1 -1 1 1 // original 1 1 0 1 // replacing -1's with 0's 11 1 // result, as it was returned from convert_FANN_array_to_binary() 

I've been looking at the replaceAll code, and, I'm really not sure why it is replacing consecutive -1's with one 0, and then not returning any 0's (and some 1's) in the final result. =\

6
  • 2
    In this particular instance it looks as though another solution would be more appropriate – i.e. don’t use string operations at all, use an array of integers / bools. Commented Mar 17, 2011 at 18:01
  • It needs to be strings, because we are reading from an ascii file. Commented Mar 17, 2011 at 18:05
  • 3
    If you follow @Konrad's advice, you could use std::replace to replace values. The fact that you are reading from an ascii file is no reason not to represent your numbers as integers. Commented Mar 17, 2011 at 18:09
  • 3
    Then convert them. The flow of programs is always the same: 1. read input, 2. convert to appropriate format, 3. apply calculation, 4. convert to output format, 5. output. You are trying to skip step (2) and making your life unnecessarily hard. Strings are rarely the appropriate format for anything other than text. Commented Mar 17, 2011 at 18:12
  • agreed. We need to re-write a method in our library before we can read in a serialized file though. We'll get there. Commented Mar 17, 2011 at 18:13

6 Answers 6

59

A complete code:

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 a more 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 changed: abc abc def ReplaceStringInPlace() input string modified: a?? a?? def 
Sign up to request clarification or add additional context in comments.

3 Comments

ReplaceString(string("abc\dir\dir1"), string("\\"), string("\\\\")); didn't work
You should actually check if search string is empty, otherwise an endless loop will occur.
To fix the infinite loop, I suggest changing "pos += replace.length();" with "pos += replace.length() + (search.empty() ? 1 : 0);" or the equivalent (could store (search.empty() ? 1 : 0) in a variable before the loop to avoid evaluating search.empty() each time) so that replaceString("abc", "", ":") returns ":a:b:c:". Most string replace functions I have used in other languages work this way.
21

The bug is in str.replace(start_pos, end_pos, to);

From the std::string doc at http://www.cplusplus.com/reference/string/string/replace/

string& replace ( size_t pos1, size_t n1, const string& str ); 

You are using an end-position, while the function expects a length.

So change to:

while((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); // ... } 

Note: untested.

5 Comments

BTW, I don't approve of the code nor the style, but that's not the question here.
Is there a better way to do string replace? I'm really amazed that this isn't something built into string.h.... like... seriously.. the higher level languages all have it.
@NullVoxPopuli I found your problem: C++ is not a higher level language
@Sjoerd if you don't approve of the code and style, what would you suggest as an alternative?
In C++11 there is a way std::regex_replace(str, std::regex(from), to) @NullVoxPopuli
12

C++11 now includes the header <regex> which has regular expression functionality. From the docs:

// regex_replace example #include <iostream> #include <string> #include <regex> #include <iterator> int main () { std::string s ("there is a subsequence in the string\n"); std::regex e ("\\b(sub)([^ ]*)"); // matches words beginning by "sub" // using string/c-string (3) version: std::cout << std::regex_replace (s,e,"sub-$2"); std::cout << std::endl; return 0; } 

Of course, now you have two problems.

Comments

11

This is going to go in my list of 'just use a Boost library' answers, but here it goes anyway:

Have you considered Boost.String? It has more features than the standard library, and where features overlap, Boost.String has a more much more natural syntax, in my opinion.

1 Comment

The function he's looking for.
11

I found the replace functions given in previous answers, all using in-place str.replace() call internally, very slow when working with a string of about 2 MB length. Specifically, I called something like ReplaceAll(str, "\r", ""), and on my particular device, with the text file containing a lot of newlines, it took about 27 seconds. I then replaced it with function just concatenating sub-strings in a new copy, and it took only about 1 seconds. Here is my version of ReplaceAll():

void replaceAll(string& str, const string& from, const string& to) { if(from.empty()) return; string wsRet; wsRet.reserve(str.length()); size_t start_pos = 0, pos; while((pos = str.find(from, start_pos)) != string::npos) { wsRet += str.substr(start_pos, pos - start_pos); wsRet += to; pos += from.length(); start_pos = pos; } wsRet += str.substr(start_pos); str.swap(wsRet); // faster than str = wsRet; } 

Greg

7 Comments

You can squeeze a little more efficiency out of this by doing str.swap(wsRet) at the end instead of an assignment. This can cheaply exchange the strings' contents instead of performing a possibly expensive copy.
@Blastfurnace, indeed, thank you for this tip! I tested it, works fine. Will update the sample code above with it as well.
I have a function very similar to this and you are right about the performance. When from and to are different sizes this can be very fast. If they are the same size then the common, in place, version works well.
Indeed, for example I use a specialized function to replace in place single chars. I guess the moving around the rest of the string with each replacement is what causes delays, when the lengths are not equal. Thanks!
Why no C++11 syntax? Very confusing for newbie with all the errors coming up.
|
1

Try this:

#include <string> string replace_str(string & str, const string & from, const string & to) { while(str.find(from) != string::npos) str.replace(str.find(from), from.length(), to); return str; } 

2 Comments

this look will never terminate if to contains from.
This is calling str.find() twice per iteration, which is redundant. But more importantly, it is searching the str from the very beginning on each call. string::find() has an optional parameter to specify the index to start searching from, use it so you don't have to re-search the portion of the string that you have already processed.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.