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 need a method that helps me to find a string inside another substring, or in other words, find a string inside a subrange of other string. Besides, I need to find it in reverse order because I know that the string I'm looking for is closed to the end of the substring used as "haystack".
Let's suppose the following piece of code, where rfind_in_substr is the method I'm asking for:
std::string sample("An example with the example word example trice");
// substring "ample with the example wo"
std::size_t substr_beg = 5;
std::size_t substr_size = 24;
// (1)
std::size_t pos = rfind_in_substr(sample, substr_beg,
substr_size, "example");
// pos == 20, because its the index of the start of the second
// "example" word inside the main string.
Of course, the line (1) could be replaced by:
std::size_t pos = substr_beg + sample.substr
(substr_beg, substr_size).rfind("example");
But that implies an unnecesary copy of the substring. Is there any method or C++/boost method that could help me doing that?
I was looking at boost::algorithm::string library but I've found nothing (that I had understood). I know that C++17 has the std::string_view class, that would be perfect, but I'm using C++14.
From Boost.StringAlgo:
#include <boost/algorithm/string/find.hpp>
auto haystack = boost::make_iterator_range(str.begin() + from, str.begin() + from + len);
auto found = boost::algorithm::find_last(haystack, needle);
Now, if you need to use this with other member functions from std::string, you need to do extra steps in converting a resulting range into an index like this answer does, but if you aren't, then simply use the range interface and avoid the std::string's "helpful" methods.
Another option is to use boost::string_ref which is what std::string_view is basically based on:
#include <iostream>
#include <boost/utility/string_ref.hpp>
std::size_t rfind_in_substr(std::string const& str, std::size_t from,
std::size_t len, std::string const& s)
{
return from + boost::string_ref(str).substr(from, len).rfind(s);
}
int main()
{
std::string sample("An example with the example word example trice");
// substring "ample with the example wo"
std::size_t substr_beg = 5;
std::size_t substr_size = 24;
// (1)
std::size_t pos = rfind_in_substr(sample, substr_beg,
substr_size, "example");
// pos == 20, because its the index of the start of the second
// "example" word inside the main string.
std::cout << pos << "\n";
}
You can find the answer by combining an API that limits the search within the original string by length and an additional check to see if the end result comes prior to substr_beg:
std::size_t rfind_in_substr(
const std::string& str
, const std::size_t from
, const std::size_t len
, const std::string& sub
) {
std::size_t res = str.rfind(sub, from+len-sub.size());
return res != string::npos && res >= from ? res : string::npos;
}
from+len-sub.size() computes the last position at which the substring could start.
res >= from rejects an answer if it comes before the initial character of substring.
Demo.
With std::find_end the problem can be solved efficiently without using more than needed, but I hoped there was any method that already solved that:
#include <iostream>
#include <string>
#include <algorithm>
std::size_t rfind_in_substr(std::string const& str, std::size_t from,
std::size_t len, std::string const& s)
{
auto sub_beg = str.begin() + from;
auto sub_end = sub_beg + len;
auto found_it = std::find_end(sub_beg, sub_end, s.begin(), s.end());
if (found_it == sub_end)
return str.npos;
else
return found_it - str.begin();
}
int main()
{
std::string sample("An example with the example word example trice");
// substring "ample with the example w"
std::size_t substr_beg = 5;
std::size_t substr_size = 24;
std::size_t pos = rfind_in_substr(sample, substr_beg,
substr_size, "example");
std::cout << pos << std::endl; // Prints 20
}
I am quite new to C++ and i think i made a tiny mistake somewhere in this bit of code. I failed to spot it so far. I hope you can help me, and tell me how/where/why it is wrong?
Many thanks in advance.
The code:
std::vector<std::string> spliter(const std::string& s, char delimiter)
{
std::vector<std::string> result;
size_t start = 0;
for(std::size_t i = 0; i != std::string::npos; i = s.find(delimiter,start))
{
result.push_back( s.substr(start,i-start) );
start = i+1;
}
iprintf("\x1b[2J");
printf("\x1b[4;0HDone Splitting Text.");
swiWaitForVBlank();
return result;
}
Parameters given:
s = "$ 00-000 SS ''Prologue'' CF N00-001 V 1 MP 20"
delimiter = ' ' (a space)
Expected result:
result[0] = $
result[1] = 00-000
result[2] = SS
etc.
Current wrong result:
result[0] =
result[1] =
result[2] = 00-000
etc.
Any help is greatly appreciated!
I believe the problem is in the loop.. You start from 0, and the first thing that you push is from 0 to 0.
size_t start = 0;
for(std::size_t i = 0; i != std::string::npos; i = s.find(delimiter,start))
{
result.push_back( s.substr(start,i-start) );
start = i+1;
}
instead if you start i from s.find(delimiter, start) it should work. Example here..
Here is a possible way to fix your algorithm:
#include <vector>
#include <string>
std::vector<std::string> spliter(const std::string& s, char delimiter)
{
std::vector<std::string> result;
std::string::size_type start = 0;
auto pos = s.find(delimiter, 0);
while (pos != std::string::npos)
{
result.push_back(s.substr(start, pos - start));
start = pos + 1;
pos = s.find(delimiter, start);
}
if (start < s.length())
{
result.push_back(s.substr(start));
}
return result;
}
And here is a live example of this algorithm giving the correct output for your test string.
Notice, that you could generalize this to work with a string as a delimiter rather than a single character just by changing the type of splitter's second argument (and passing " " instead of ' ', of course).
for example we have in our set:
bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog
bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog
bin/obj/Debug/vc100.idb
bin/obj/Debug/vc100.pdb
So this is what I tried based on this grate answer:
#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <iterator>
using namespace std;
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length() + 1;
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
};
void directory_listning_without_directories_demo()
{
set<string> output;
set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");
std::transform(demo_set.begin(),
demo_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug/"));
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
int main()
{
directory_listning_without_directories_demo();
cin.get();
return 0;
}
This outputs:
CloudServerPrototype/
file1
file2
folder/
folder/folder/
vc100.idb
vc100.pdb
and we are given with bin/obj/Debug/string. We want to cout:
vc100.idb
vc100.pdb
CloudServerPrototype/
How to do such thing?
Quick example of what you want to do.
String.find(): http://www.cplusplus.com/reference/string/string/find/
String.subStr(): http://www.cplusplus.com/reference/string/string/substr/
string str = "bin/obj/Debug/vc100.pdb";
string checkString ("bin/obj/Debug");
// Check if string starts with the check string
if (str.find(checkString) == 0){
// Check if last letter if a "/"
if(str.substr(str.length()-1,1) == "/"){
// Output strating at the end of the check string and for
// the differnce in the strings.
cout << str.substr(checkString.length(), (str.length() - checkString.length()) ) << endl;
}
}
It's not clear with which part of the problem you are stuck, so here is a starter for you.
To get the parts of the strings between "given string" and the final '/' (where present):
std::string get_pertinent_part(const std::string& s)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length() + 1;
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
To insert these parts into a new set (output) to guarantee uniqueness you can use the following:
std::transform(your_set.begin(),
your_set.end(),
std::inserter(output, output.end()),
get_pertinent_part);
You may wish to pass given_string into get_pertinent_part(), in which case you'll need to convert it to a functor:
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s)
{
std::string::size_type first = 0;
//
// ...same code as before...
//
return s.substr(first, count);
}
};
You can then call it this way:
std::transform(your_set.begin(),
your_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug"));
To output the new set:
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
Sorting the results is left as an exercise.
The easiest way I can think of, using the standard C functions, would be:
char * string1 = "bin/obj/Debug"
char * string2 = "bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog"
char result[64];
// the above code is just to bring the strings into this example
char * position = strstr(string1, string2);
int substringLength;
if(position != NULL){
position += strlen(string2);
substringLength = strchr(position, '/') - position;
strncpy(result, position, substringLength);
}else{
strcpy(result, string1); // this case is for when your first string is not found
}
cout << result;
The first thing that occurs, is finding the substring, string1, in the string we are analyzing, being string2. Once we found the starting point, and assuming it was there at all, we add the length of that substring to that starting point using pointer arithmatic, and then find the resulting string's length by subtracting the starting position from the ending position, which is found with strchr(position, '/'). Then we simply copy that substring into a buffer and it's there to print with cout.
I am sure there is a fancy way of doing this with std::string, but I'll leave that to anyone who can better explain c++ strings, I never did manage to get comfortable with them, haha
I'm tokening with the following, but unsure how to include the delimiters with it.
void Tokenize(const string str, vector<string>& tokens, const string& delimiters)
{
int startpos = 0;
int pos = str.find_first_of(delimiters, startpos);
string strTemp;
while (string::npos != pos || string::npos != startpos)
{
strTemp = str.substr(startpos, pos - startpos);
tokens.push_back(strTemp.substr(0, strTemp.length()));
startpos = str.find_first_not_of(delimiters, pos);
pos = str.find_first_of(delimiters, startpos);
}
}
The C++ String Toolkit Library (StrTk) has the following solution:
std::string str = "abc,123 xyz";
std::vector<std::string> token_list;
strtk::split(";., ",
str,
strtk::range_to_type_back_inserter(token_list),
strtk::include_delimiters);
It should result with token_list have the following elements:
Token0 = "abc,"
Token1 = "123 "
Token2 = "xyz"
More examples can be found Here
I now this a little sloppy, but this is what I ended up with. I did not want to use boost since this is a school assignment and my instructor wanted me to use find_first_of to accomplish this.
Thanks for everyone's help.
vector<string> Tokenize(const string& strInput, const string& strDelims)
{
vector<string> vS;
string strOne = strInput;
string delimiters = strDelims;
int startpos = 0;
int pos = strOne.find_first_of(delimiters, startpos);
while (string::npos != pos || string::npos != startpos)
{
if(strOne.substr(startpos, pos - startpos) != "")
vS.push_back(strOne.substr(startpos, pos - startpos));
// if delimiter is a new line (\n) then addt new line
if(strOne.substr(pos, 1) == "\n")
vS.push_back("\\n");
// else if the delimiter is not a space
else if (strOne.substr(pos, 1) != " ")
vS.push_back(strOne.substr(pos, 1));
if( string::npos == strOne.find_first_not_of(delimiters, pos) )
startpos = strOne.find_first_not_of(delimiters, pos);
else
startpos = pos + 1;
pos = strOne.find_first_of(delimiters, startpos);
}
return vS;
}
I can't really follow your code, could you post a working program?
Anyway, this is a simple tokenizer, without testing edge cases:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void tokenize(vector<string>& tokens, const string& text, const string& del)
{
string::size_type startpos = 0,
currentpos = text.find(del, startpos);
do
{
tokens.push_back(text.substr(startpos, currentpos-startpos+del.size()));
startpos = currentpos + del.size();
currentpos = text.find(del, startpos);
} while(currentpos != string::npos);
tokens.push_back(text.substr(startpos, currentpos-startpos+del.size()));
}
Example input, delimiter = $$:
Hello$$Stack$$Over$$$Flow$$$$!
Tokens:
Hello$$
Stack$$
Over$$
$Flow$$
$$
!
Note: I would never use a tokenizer I wrote without testing! please use boost::tokenizer!
if the delimiters are characters and not strings, then you can use strtok.
It depends on whether you want the preceding delimiters, the following delimiters, or both, and what you want to do with strings at the beginning and end of the string that may not have delimiters before/after them.
I'm going to assume you want each word, with its preceding and following delimiters, but NOT any strings of delimiters by themselves (e.g. if there's a delimiter following the last string).
template <class iter>
void tokenize(std::string const &str, std::string const &delims, iter out) {
int pos = 0;
do {
int beg_word = str.find_first_not_of(delims, pos);
if (beg_word == std::string::npos)
break;
int end_word = str.find_first_of(delims, beg_word);
int beg_next_word = str.find_first_not_of(delims, end_word);
*out++ = std::string(str, pos, beg_next_word-pos);
pos = end_word;
} while (pos != std::string::npos);
}
For the moment, I've written it more like an STL algorithm, taking an iterator for its output instead of assuming it's always pushing onto a collection. Since it depends (for the moment) in the input being a string, it doesn't use iterators for the input.