how to point to delimiter at fixed position in std::string - c++

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;
}

Related

Finding index of needle in haystack recursively.

I am pretty close to completing my function. I need to take 2 strings, and return the index of string 2 inside of string 1. I know there is a find function for this, but I am not able to use it. It also has to be done with recursive programming.
I have the following.
int index_of(string haystack, string needle) {
int index = 0;
string test = haystack.substr(index, needle.length());
if (test == needle) {
return index;
}
else {
return 1 + index_of(haystack.substr(1), needle);
}
}
It returns the index of the needle in the haystack no problem but there are 2 things it needs to do I can not figure out.
1) If the needle is not in the haystack, then it needs to return a -1. I have done it so at the end if it does not exist, it returns a -1, but because it is recursive, it then adds the other times it returned 1. I am not sure how to just return a single value at the end without adding all the other instances on it.
2) I am suppose to use a helper function within it and I am not sure how to do that either.
Thanks for any help!
In general, you want to return the value of a recursive function unadulterated. In your case, this:
return 1 + index_of(some_parameters);
Should be this:
return index_of(some_parameters);
Now, you just need to choose parameters such that you can keep track of the index until you need to return it, or alternatively -1.
One such function might have the constructor:
index_of(string haystack, string needle, int index);
Here is a demonstrative program that shows how the function can be implemented.
#include <iostream>
#include <string>
std::string::size_type index_of( std::string haystack, const std::string &needle )
{
if ( haystack.size() < needle.size() ) return std::string::npos;
if ( haystack.compare( 0, needle.size(), needle ) == 0 ) return 0;
std::string::size_type index;
return ( index = index_of( haystack.substr( 1 ), needle ) ) == std::string::npos ? index : ++index;
}
int main()
{
std::string haystack( "asdfghjkl" );
std::string needle( "gh" );
std::string::size_type index = index_of( haystack, needle );
if ( index != std::string::npos )
{
std::cout << "string \"" << needle
<< "\" is found in string \"" << haystack
<< "\" at position " << index
<< std::endl;
}
else
{
std::cout << "string \"" << needle
<< "\" is not found in string \"" << haystack << "\""
<< std::endl;
}
}
Its output is
string "gh" is found in string "asdfghjkl" at position 4
Of course the simplest approach is to define a static variable that would keep the current position in the source string. But in this case I do not think that such a function is a "pure recursive".

Vector Iterator not Incrementable In for loop

Alright so I know people have gone on and on about this topic, but I've searched question after question and edited my code repeatedly, and I still seem to be having this problem.
This piece of code is basically intended to run through a vector and compare/combine its contents. In order to do that, I use 'it' and 'in' to go through the vector and .erase to delete contents that have been combined into new parts of the vector. Knowing I would basically need the .erase function and iterators, I used some code (the auto it stuff) from other questions on StackOverflow that seemed to work in this situation. Since I'm unfamiliar with that code, I may have used it incorrectly for this situation, though I'm not sure about that.
Anyway, as the title indicates, I'm getting a 'vector iterator not incrementable' error. The program seems to be running into this error after going through the second for loop a few times. I've tried a lot of things, but I just can't seem to figure out what part of my code is the real problem.
Any help would be greatly appreciated!
\if (!strvector.empty()) {
for (auto in = strvector.begin(); in != strvector.end() - 1;) {//in order to process if there is more than one final piece
str = ' ' + *in + ' ';//adds spaces so it only compares beginnings & ends
if (strvector.size() < 2) {
break;
}//doesn't do anything if there are too few objects to process
for (auto it = strvector.begin(); it != strvector.end() - 1;) {
if (strvector.size() < 2) {
break;
}//doesn't continue if there are too few objects to process
str2 = ' ' + *it + ' '; //adds spaces so it only compares beginnings & ends
substr = str; //sets substring of string to be chopped up
while (substr.length() >= 6) { //only processes substrings >= 6 characters bc smaller bits are useless
size_t found = str2.find(substr); //searches str2 for substr
if (found != string::npos) {
str = str.substr(0, substr.length()) + ' '; //cuts substr off of str
str.append(str2); //adds str and str2
it = strvector.erase(it);
substr = 'a'; //shortens substr to get out of while
test=1;
}//end if
else {
substr.erase(substr.size() - 1, 1); //if substr was not found, decrement substr and compare again
}
}//end while
substr = str; //resets substr
while (substr.length() >= 6) { //only processes substrings >= 6 characters bc smaller bits are useless
size_t found = str2.find(substr); //searches str2 for substr
if (found != string::npos) {
str = str.substr(substr.length()) + ' '; //cuts substr from beginning of string
str = str2 + str; //adds str2 and str, with str2 at the beginning
it = strvector.erase(it++);
substr = 'a'; //shortens substr to get out of while
test=1;
}//end if
else {
substr.erase(0, 1); //erases the first character of the string
}
if (test < 1) {
it++; //increments if the erase function did not already do that
}
}//end while
if (test != 1) {
it++; //increments if the erase function did not already do that
}
if (test < 2) {
strvector.push_back(str); //adds new str to the vector
test = 0;
}
}//end ptr2 for
if (strvector.size() < 2) {
in = strvector.erase(in - 1);
}
else {
in++;
}
cout << "str1 is " << str << endl; //prints out str
}//end ptr for
}//end if vector is not empty
Once you call erase on a std::vector the iterator that you're using becomes invalid. Instead the call to erase returns a brand new iterator that you should use going forward.
In your case, you're using the postfix ++ operator which will attempt to increment the iterator after it has been used by the erase method. At that point the iterator is invalid and so you get an error.
What you likely want is it = strvector.erase(it); which removes the element at the iterator and then returns a new iterator positioned at the element after the one you erased. You don't need the additional ++ because erase effectively did that for you.

How do I get part of a char*?

I have the following code that solves a small image using Tesseract.
char *answer = tess_api.GetUTF8Text();
I know beforehand that the result will always start with the character '+' and it's one word so I want to get rid of any junk it finds.
I get the result as "G+ABC S\n\n" and I need only +ABC. So basically I need to ignore anything before + and everything after the first space. I was thinking I should use rindex to find the position of + and spaces.
std::string ParseString(const std::string& s)
{
size_t plus = s.find_first_of('+');
size_t space = s.find_first_of(" \n", plus);
return s.substr(plus, space-plus);
}
int main()
{
std::cout << ParseString("G+ABC S\n\n").c_str() << std::endl;
std::cout << ParseString("G +ABC\ne\n").c_str() << std::endl;
return 0;
}
Gives
+ABC
+ABC
If you really can't use strings then something like this might do
char *ParseString2(char *s)
{
int plus,end;
for (plus = 0 ; s[plus] != '+' ; ++plus){}
for (end = plus ; s[end] != ' ' && s[end] != '\n' ; ++end){}
char *result = new char[end - plus + 1];
memcpy(result, s + plus, end - plus);
result[end - plus] = 0;
return result;
}
You can use:
// just scan "answer" to find out where to start and where to end
int indexStart = // find the index of '+'
int indexEnd = // find the index before space
int length = indexEnd-indexStart+1;
char *dataYouWant = (char *) malloc(length+1); // result will be stored here
memcpy( dataYouWant, &answer[indexStart], length );
// for example answer = "G+ABC S\n\n"
dataYouWant[length] = '\0'; // dataYouWant will be "+ABC"
You can check out Strings in c, how to get subString for other alternatives.
P.S. suggestion: use string instead in C++, it will be much easier (check out #DavidSykes's answer).

Replacing all occurrences of a substring with another substring in a long string

I'm writing a function, which takes three parameters:
target : Target String
oldVal : Old substring
newVal : New Substring (To replace the oldVal)
The task of this function is to find all the occurrence of oldVal in target string, and replace them with newVal.
This is the function I've got at the moment:
std::string replace_old_with_new(std::string target, std::string oldVal, std::string newVal) {
std::cout << "target : " << target << ", oldVal: " << oldVal << ", newVal: " << newVal << "\n";
std::string::iterator begin = target.begin();
std::string::iterator oldValBegin = oldVal.begin();
while (begin != target.end()) {
if (*begin == *oldValBegin) {
target = target.replace(begin, begin + oldVal.size(), oldVal);
begin = target.begin();
} else {
++begin;
}
}
return target;
}
The following call to the above function:
replace_old_with_new("Hello! hi hi!", "hi", "bye");
should return the string -
"Hello! bye bye!"
But, when I run the code, nothing happens. It seems like I'm stuck in an infinite loop. The cursor keeps blinking on terminal. Is something wrong with my function. I think what might be troubling is the replace call in the if block. Is that the correct way to use iterator range in the replace function call? I can do this with erase and insert. But I want to use replace here.
Strings are much smarter than you give them credit for. They know how to search, so you don't have to do it yourself.
int pos = 0;
int match_pos;
std::string result;
while ((match_pos = target.find(oldVal, pos)) != std::string::npos) {
result += target.substr(pos, match_pos - pos);
result += newVal;
pos = match_pos + target.size();
}
result += target.substr(pos, std::string::npos);
Sorry, this is a sketch; not tested, but you get the idea.

Preventing code duplication in and outside of a loop

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 );