Preventing code duplication in and outside of a loop - c++

I have a problem rewriting a loop:
else if( "d" == option || "debug" == option )
{
debug(debug::always) << "commandline::set_internal_option::setting debug options: "
<< value << ".\n";
string::size_type index = 0;
do
{
const string::size_type previous_index = index+1;
index=value.find( ',', index );
const string item = value.substr(previous_index, index);
debug::type item_enum;
if( !map_value(lib::debug_map, item, item_enum) )
throw lib::commandline_error( "Unknown debug type: " + item, argument_number );
debug(debug::always) << "commandline::set_internal_option::enabling " << item
<< " debug output.\n";
debug(debug::always) << "\n-->s_level=" << debug::s_level << "\n";
debug::s_level = static_cast<debug::type>(debug::s_level ^ item_enum);
debug(debug::always) << "\n-->s_level=" << debug::s_level << "\n";
} while( index != string::npos );
}
value is something like string("commandline,parser") and the problem is that in the first run, I need substr(previous_index, index), but in every subsequent iteration I need substr(previous_index+1, index) to skip over the comma. Is there some easy way I'm overlooking or will I have to repeat the call to find outside the loop for the initial iteration?

Since your goal is to prevent code duplication:
std::vector<std::string> v;
boost::split(v, value, [](char c) { c == ','; });
If you want to create your own split function, you can do something like this:
template<typename PredicateT>
std::vector<std::string> Split(const std::string & in, PredicateT p)
{
std::vector<std::string> v;
auto b = in.begin();
auto e = b;
do {
e = std::find_if(b, in.end(), p);
v.emplace_back(b,e);
b = e + 1;
} while (e != in.end());
return v;
}

Why not update previous_index after taking the substr?
string::size_type index = 0;
string::size_type previous_index = 0;
do {
index=value.find( ',', previous_index );
const string item = value.substr(previous_index, index);
previous_index = index+1;
} while( index != string::npos );
Unchecked, but this should do the trick (with only one more word of memory).

Start at -1?
string::size_type index = -1;
do
{
const string::size_type previous_index = index + 1;
index=value.find(',', previous_index);
const string item = value.substr(previous_index, index - previous_index);
} while( index != string::npos );

A stupid (and somewhat unreadable) solution would be something like this:
string::size_type once = 0;
/* ... */
const string::size_type previous_index = index+1 + (once++ != 0); // or !!once

First, I think there's a small error:
In your code, the expression index=value.find( ',', index ); doesn't change the value of index if it already is the index of a comma character within the string (which is always the case except for the first loop iteration).
So you might want to replace while( index != string::npos ); with while( index++ != string::npos ); and previous_index = index+1 with previous_index = index.
This should also solve your original problem.
To clarify:
string::size_type index = 0;
do
{
const string::size_type previous_index = index;
index = value.find( ',', index );
const string item = value.substr(previous_index, index - previous_index);
} while( index++ != string::npos );

Related

Extract information from std::string

Too many string related queries yet some doubt remains, for each string is different and each requirement is different too.
I have a single string in this form:
Random1A:Random1B::String1 Random2A:Random2B::String2 ... RandomNA:RandomNB::StringN
And I want to get back a single string in this form:
String1 String2 ... StringN
In short, the input string would look like A:B::Val1 P:Q::Val2, and o/p result string would look like "Val1 Val2".
PS: Randoms and Strings are small (variable) length alphanumeric strings.
std::string GetCoreStr ( std::string inputStr, int & vSeqLen )
{
std::string seqStr;
std::string strNew;
seqStr = inputStr;
size_t firstFind = 0;
while ( !seqStr.empty() )
{
firstFind = inputStr.find("::");
size_t lastFind = (inputStr.find(" ") < inputStr.length())? inputStr.find(" ") : inputStr.length();
strNew += inputStr.substr(firstFind+2, lastFind-firstFind-1);
vSeqStr = inputStr.erase( 0, lastFind+1 );
}
vSeqLen = strNew.length();
return strNew;
}
I want to get back a single string String1 String2 ... StringN.
My code works and I get result of my choice, but it is not an optimal form. I want help in improving the code quality.
I ended up doing it the C-way.
std::string GetCoreStr ( const std::string & inputStr )
{
std::string strNew;
for ( int i = 0; i < inputStr.length(); ++i )
{
if ( inputStr[i] == ':' && inputStr[i + 1] == ':' )
{
i += 2;
while ( ( inputStr[i] != ' ' && inputStr[i] != '\0' ) )
{
strNew += inputStr[i++];
}
if ( inputStr[i] == ' ' )
{
strNew += ' ';
}
}
}
return strNew;
}
I am having trouble deciding on how to adjust the offset. [...]
std::string getCoreString(std::string const& input)
{
std::string result;
// optional: avoid reallocations:
result.reserve(input.length());
// (we likely reserved too much – if you have some reliable hint how many
// input parts we have, you might subtract appropriate number)
size_t end = 0;
do
{
size_t begin = input.find("::", end);
// added check: double colon not found at all...
if(begin == std::string::npos)
break;
// single character variant is more efficient, if you need to find just such one:
end = std::min(input.find(' ', begin) + 1, input.length());
result.append(input.begin() + begin + 2, input.begin() + end);
}
while(end < input.length());
return result;
}
Side note: you do not need the additional 'length' output parameter; it's redundant, as the returned string contains the same value...

How can I parse specified fields from given txt query

C++ question:
I have the query in txt like this:
CUSTOMER_ID=4155&ORDER_ITEM_TYPE_ID=1&ORDER_ITEM_TYPE_NAME=Product&ORDER_ITEM_SKU=&ORDER_CURRENCY_CODE=UAH
How can I get these fields to make it look nearly like:
Customer_ID: 4155
ORDER_ITEM_TYPE_ID: 1
// and etc...
I know there is a separator "&" between each field, but I don't know how to do it properly.
Use methods of std::string like find,substr. Example:
const std::string field = "CUSTOMER_ID=4155&ORDER_ITEM_TYPE_ID=1&ORDER_ITEM_TYPE_NAME=Product&ORDER_ITEM_SKU=&ORDER_CURRENCY_CODE=UAH";
const char separator = '&';
const char equal = '=';
std::string::size_type cur = 0;
while ( cur != std::string::npos )
{
std::string name;
std::string value;
std::string::size_type newPos = field.find(equal, cur);
if ( newPos != std::string::npos )
{
name = field.substr( cur, newPos - cur );
cur = newPos + 1;
newPos = field.find(separator, cur);
value = field.substr( cur, ( newPos == std::string::npos ) ? std::string::npos : newPos - cur );
cur = ( newPos == std::string::npos ) ? std::string::npos : newPos + 1;
std::cout << name << ": " << value << std::endl;
}
else
{
break;
}
}
try
`char str[10][20];
int len = 0;
int count = 0;
while((c = string[i])!= EOC )
/* EOC : End of Character */
{
if (c == '&') {
str[count][len] = '\0';
len = 0;
count++;
continue;
}
str[count][len] = c;
len++;
}
Replace all '&' with replace and then when you write the data after this back into a file it's formated as requested.
std::ifstream infile("thefile.txt");
while (std::getline(infile, line)) //Read File line by line
{
std::replace( line.begin(), line.end(), '&', '\0'); // replace all '&' to '\0'
cout << line << endl;
}

how to point to delimiter at fixed position in std::string

Say I have text say with '#' as a delimiter.
example
std::string key = "012#txt1#txt2#txt3#txt4# #some other text:"
I have to insert modified text between #at position 5 and #at position 6. The one shown above with spaces in between.
To accomplish this I need to find 5th # and 6th #.
I wrote a small code but its not doing what i expect to do.It always return first found '#'. can someone please advice me.
std::string temp = key;
size_t found = 0;
size_t pos_key = temp.find('#');
while( ( found !=5 )&& ( pos_key != std::string::npos ) )
{
found++;
temp.find_first_of('#', pos_key + 1 );
temp.erase(0, pos_key );
}
std::cout << " the pos key is " << pos_key << std::endl ;
There are a couple problems going on. first you never update pos_key so you are stomping all over your string when you call erase which I am not sure why you are doing that. If you need to find the nth symbol you can use a function like:
size_t find_nth(const std::string & line, const std::string & symbol, size_t nth)
{
size_t pos = 0;
size_t counter = 0;
while (counter < nth && (pos = line.find(symbol, pos)) != std::string::npos)
{
counter++; // found a match so increment
pos++; // increment so we search for the next one
}
return pos;
}
And you can see it running in this Live Example
It seems you have two problems.
First you are not remembering the position of the '#' when you find it, you need to assign the return value of the std::string::find_first_of function to pos_key.
Second you keep deleting the contents of the string up to the position you find. That throws off all the position information you got from the std::string::find_first_of function.
I think this might be what you need:
int main()
{
std::string key = "012#txt1#txt2#txt3#txt4# #some other text:";
std::string temp = key;
size_t found = 0;
size_t pos_key = temp.find('#');
while((found != 5) && (pos_key != std::string::npos))
{
found++;
// this line does nothing with the found position
// temp.find_first_of('#', pos_key + 1);
// instead record the position of the latest '#'
pos_key = temp.find_first_of('#', pos_key + 1);
// this line just deletes most of the string
// for no apparent reason
// temp.erase(0, pos_key);
}
std::cout << " the pos key is " << pos_key << std::endl;
}

Finding all wanted words in a string

I have a string which is too long, I want to find and locate all of the wanted words. For example I want to find the locations of all "apple"s in the string. Can you tell me how I do that?
Thanks
Apply repeatedly std::string::find if you are using C++ strings, or std::strstr if you are using C strings; in both cases, at each iteration start to search n characters after the last match, where n is the length of your word.
std::string str="one apple two apples three apples";
std::string search="apple";
for(std::string::size_type pos=0; pos<str.size(); pos+=search.size())
{
pos=str.find(search, pos);
if(pos==std::string::npos)
break;
std::cout<<"Match found at: "<<pos<<std::endl;
}
(link)
Use a loop which repeatedly calls std::string::find; on each iteration, you start finding beyond your last hit:
std::vector<std::string::size_type> indicesOf( const std::string &s,
const std::string &needle )
{
std::vector<std::string::size_type> indices;
std::string::size_type p = 0;
while ( p < s.size() ) {
std::string::size_type q = s.find( needle, p );
if ( q == std::string::npos ) {
break;
}
indices.push_back( q );
p = q + needle.size(); // change needle.size() to 1 for overlapping matches
}
return indices;
}
void findApples(const char* someString)
{
const char* loc = NULL;
while ((loc = strstr(someString, "apple")) != NULL) {
// do something
someString = loc + strlen("apple");
}
}

Replace substring with another substring C++

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;