C++ find word in string multiple times - c++

I'm currently trying to find a word inside a string. I'm using string::find(). However this only finds the word in the string for one time.
string a = "Dog";
string b = "I have a dog, his name is dog";
if (b.find(a) != string::npos)
{
...
}
Is there a way to scan string b to see how many times the word "dog" appears?

Use a loop until you can't find anymore.
std::size_t count = 0, pos = 0;
while ((pos = b.find(a, pos)) != std::string::npos) {
pos += a.size(); // Make sure the current one is skipped
count++;
}
// Now you have count

Related

Having issues finding multiple substrings within a string

I am trying to write a program that compares two strings (string and substring) and incitements each time the substring is found within the string. However, using the standard:
if(str.find(substr) != string::npos)
{
count++;
}
I run into the problem that if the substring appears multiple times in the string it only increments once. So if the string is "test test test test" and the substring is "test" count only ends up being 1 instead of 4.
What would be the best way to fix this?
*Notes for context:
1) At one point I was checking the string character by character to see if they matched, but had to scrap that when I ran into issues when some words had smaller words in them.
Example: 'is' would get picked up inside the word 'this', etc
2)The larger program that this is for accepts two vectors. The first vector has a string for each element being sentences the user get to type in (acting at the main string in the example above). And the second vector has each word from all the sentences entered into the first vector (acting as the substring in the example above). Not sure if that bit matters or not, but figured I would throw it in there
Example:
vector<string> str {this is line one, this is line two, this is line three};
vector<string> substr {is, line, one, this, three, two};
3) I'm thinking if there was some way of doing the opposite of !=string::npos would work, but not sure if that even exist.
You need a loop to find all of the occurances of a substring in a given string.
However, since you want to differentiate substrings that are whole words from substrings in larger words, you need to parse the string to determine the whole words before you compare them.
You can use std::string::find_first_of() and std::string::find_first_not_of() to find the beginning and ending indexes of each whole word between desired delimiters (whitespace, punctuation, etc). You can use std::string::compare() to compare a substring between those two indexes to your desired substring. For example:
#include <string>
const std::string delims = ",. ";
size_t countWord(const std::string &str, const std::string &word)
{
std::string::size_type start = 0, end;
size_t count = 0;
while ((start = str.find_first_not_of(delims, start)) != std::string::npos)
{
end = str.find_first_of(delims, start+1);
if (end == std::string::npos)
{
if (str.compare(start, str.size()-start, word) == 0)
++count;
break;
}
if (str.compare(start, end-start, word) == 0)
++count;
start = end + 1;
}
return count;
}
Alternatively, you can extract the whole words into a std::vector and then use std::count() to count how many elements match the substring. For example:
#include <string>
#include <vector>
#include <algorithm>
const std::string delims = ",. ";
size_t countWord(const std::string &str, const std::string &word)
{
std::vector<std::string> vec;
std::string::size_type start = 0, end;
while ((start = str.find_first_not_of(delims, start)) != string::npos)
{
end = str.find_first_of(delims, start+1);
if (end == std::string::npos)
{
vec.push_back(str.substr(start));
break;
}
vec.push_back(str.substr(start, end-start));
start = end + 1;
}
return std::count(vec.begin(), vec.end(), word);
}

Trying index characters in strings and create new strings

I'm currently making a quick Hangman game and I'm struggling to take the correctly guessed letters from the word and insert them into the string that I show the user as they play the game. This is my code so far:
std::string word_to_guess = "ataamataesaa";
std::string word_to_fill(word_to_guess.length(), '-');
char user_guess = 'a';
for (auto &character : word_to_guess) {
if (character == user_guess) {
std::size_t index = word_to_guess.find(&character);
word_to_fill[index] = user_guess;
}
}
std::cout << word_to_fill;
This almost works however it ignores the last two As of the string to guess which I cannot understand.
"Find" will return only the first occurrence.
Instead of iterating over characters, iterate simultaneously over the indexes of both word_to_guess and word_to_fill.
for (int i = 0 ; i < word_to_fill.length ; ++i) {
if (word_to_guess[i] == user_guess) {
word_to_fill[i] = user_guess;
}
}

string::replace not working correctly 100% of the time?

I'm trying to replace every space character with '%20' in a string, and I'm thinking of using the built in replace function for the string class.
Currently, I have:
void replaceSpace(string& s)
{
int len = s.length();
string str = "%20";
for(int i = 0; i < len; i++) {
if(s[i] == ' ') {
s.replace(i, 1, str);
}
}
}
When I pass in the string "_a_b_c_e_f_g__", where the underscores represent space, my output is "%20a%20b%20c%20e_f_g__". Again, underscores represent space.
Why is that the spaces near the beginning of the string are replaced, but the spaces towards the end aren't?
You are making s longer with each replacement, but you are not updating len which is used in the loop condition.
Modifying the string that you are just scanning is like cutting the branch under your feet. It may work if you are careful, but in this case you aren't.
Namely, you take the string len at the beginning but with each replacement your string gets longer and you are pushing the replacement places further away (so you never reach all of them).
The correct way to cut this branch is from its end (tip) towards the trunk - this way you always have a safe footing:
void replaceSpace(string& s)
{
int len = s.length();
string str = "%20";
for(int i = len - 1; i >= 0; i--) {
if(s[i] == ' ') {
s.replace(i, 1, str);
}
}
}
You're growing the string but only looping to its initial size.
Looping over a collection while modifying it is very prone to error.
Here's a solution that doesn't:
void replace(string& s)
{
string s1;
std::for_each(s.begin(),
s.end(),
[&](char c) {
if (c == ' ') s1 += "%20";
else s1 += c;
});
s.swap(s1);
}
As others have already mentioned, the problem is you're using the initial string length in your loop, but the string gets bigger along the way. Your loop never reaches the end of the string.
You have a number of ways to fix this. You can correct your solution and make sure you go to the end of the string as it is now, not as it was before you started looping.
Or you can use #molbdnilo 's way, which creates a copy of the string along the way.
Or you can use something like this:
std::string input = " a b c e f g ";
std::string::size_type pos = 0;
while ((pos = input.find(' ', pos)) != std::string::npos)
{
input.replace(pos, 1, "%20");
}
Here's a function that can make it easier for you:
string replace_char_str(string str, string find_str, string replace_str)
{
size_t pos = 0;
for ( pos = str.find(find_str); pos != std::string::npos; pos = str.find(find_str,pos) )
{
str.replace(pos ,1, replace_str);
}
return str;
}
So if when you want to replace the spaces, try it like this:
string new_str = replace_char_str(yourstring, " ", "%20");
Hope this helps you ! :)

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

Replace 3 or more occurrences of character within string

I would like to find 3 or more occurrences of a within a std::string in order to replace.
For example:
std::string foo = "This is a\n\n\n test";
std::string bar = "This is a\n\n\n\n test";
std::string baz = "This is a\n\n\n\n\n test";
std::string boo = "This is a\n\n\n\n\n\n test";
// ... etc.
Should all be converted to:
std::string expectedResult = "This is a\n\n test";
Vanilla stl would be appreciated (no regexp libs or boost) if possible.
This should find consecutive \n and replace them:
size_type i = foo.find("\n\n\n");
if (i != string::npos) {
size_type j = foo.find_first_not_of('\n', i);
foo.replace(i, j - i, "\n\n");
}
Write a function to process each string you are interested in modifying:
Read each string one character a time. Keep track of 2 char variables: a and b. For each character c you read, do:
if ( a != b ) {
a = b;
b = c;
} else if ( a == b ) {
if ( a == c ) {
// Put code here to remove c from your string at this index
}
}
I am not 100% sure if you can use something from STL directly to accomplish what you are asking, but as you can see this logic isn't much code to implement.
You can use find and replace. (this will replace "\n\n\n..." -> "\n\n"). You can pass position to string::find so that you don't have to search the start of the string again (optimization)
int pos = 0;
while ((pos = s.find ("\n\n\n", pos)) != s.npos)
s.replace (pos, 3, "\n\n", 2);
And this will replace "\n\n\n\n.." -> "\n"
int pos = 0;
while ((pos = s.find ("\n\n", pos)) != s.npos)
s.replace (pos, 2, "\n", 1);