I am creating a program that scans user input for words that are listed in an array. The find() function seems like it'll work, but I can't find anything showing how to implement it for what I want to do. I'm pretty new to programming (obviously).
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
string subj [5]={"I","He","She","We","They"};
string verb [5]={" like"," hate"," sacrifice"," smell"," eat"};
string obj [5]={" bacon","s cats","s bagels","s children","s cops"};
string greeting [5]={"How are you","How's it going","Sup dude","Hey","Hello"};
string negvibe [4]={"bad","terrible","lousy","meh"};
string userfeeling;
int main()
{
srand(time(0));
int rando = rand() %5;//generates a random number between 0 and 4
int rando1 = rand() %5;
int rando2 = rand() %5;
cout << greeting [rando1] << "." << endl;
getline(std::cin,userfeeling);
if .... // What has to be done here?
find(negvibe, negvibe + 4, userfeeling) != negvibe + 4);
// Something like that?
// then ...
{
cout << subj[rando] << verb[rando1] << obj[rando2] <<"." <<endl;
}
return 0;
}
To make find work properly you should user iterators like so
if(find(std::begin(negvibe), std::end(negvibe), userfeeling) != std::end(negvibe)){
//code you want to happen if your word is found
}
Also in your current code, the if statement doesnt actually do anything since you end it with a semicolon and not {} or leave it blank if its one line. You can see an example of the if statement as well
Below is a link to find and iterators
http://www.cplusplus.com/reference/algorithm/find/
That find function will find some element of the negvibe array that is equal to userfeeling. If you are checking whether any element of negvibe is a substring of userfeeling, you should loop through negvibe and use the std::string::find method.
bool found_negvibe = false;
for (int i = 0; i < sizeof(negvibe) / sizeof(*negvibe); i++) {
found_negvibe = found_negvibe || userfeeling.find(negvibe[i]) != string::npos;
}
Also, you don't need to specify the size of the negvibe array, you can write this:
string negvibe[] = {"bad","terrible","lousy","meh"};
One more thing, you might prefer to use a std::vector over an array, if only because c++'s faculties for getting the size of a vector are slightly more succinct than those for getting the size of an array.
vector negvibe = {"bad","terrible","lousy","meh"};
bool found_negvibe = false;
for (int i = 0; i < negvibe.size(); i++) {
found_negvibe = found_negvibe || userfeeling.find(negvibe[i]) != string::npos;
}
Related
I am faced with a simple yet complex challenge today.
In my program, I wish to insert a - character every three characters of a string. How would this be accomplished? Thank you for your help.
#include <iostream>
int main()
{
std::string s = "thisisateststring";
// Desired output: thi-sis-ate-sts-tri-ng
std::cout << s << std::endl;
return 0;
}
There is no need to "build a new string".
Loop a position iteration, starting at 3, incrementing by 4 with each pass, inserting a - at the position indicated. Stop when the next insertion point would breach the string (which has been growing by one with each pass, thus the need for the 4 slot skip):
#include <iostream>
#include <string>
int main()
{
std::string s = "thisisateststring";
for (std::string::size_type i=3; i<s.size(); i+=4)
s.insert(i, 1, '-');
// Desired output: thi-sis-ate-sts-tri-ng
std::cout << s << std::endl;
return 0;
}
Output
thi-sis-ate-sts-tri-ng
just take an empty string and append "-" at every count divisible by 3
#include <iostream>
int main()
{
std::string s = "thisisateststring";
std::string res="";
int count=0;
for(int i=0;i<s.length();i++){
count++;
res+=s[i];
if(count%3==0){
res+="-";
}
}
std::cout << res << std::endl;
return 0;
}
output
thi-sis-ate-sts-tri-ng
A general (and efficient) approach is to build a new string by iterating character-by-character over the existing one, making any desired changes as you go. In this case, every third character you can insert a hyphen:
std::string result;
result.reserve(s.size() + s.size() / 3);
for (size_t i = 0; i != s.size(); ++i) {
if (i != 0 && i % 3 == 0)
result.push_back('-');
result.push_back(s[i]);
}
Simple. Iterate the string and build a new one
Copy each character from the old string to the new one and every time you've copied 3 characters add an extra '-' to the end of the new string and restart your count of copied characters.
Like 99% problems with text, this one can be solved with a regular expression one-liner:
std::regex_replace(input, std::regex{".{3}"}, "$&-")
However, it brings not one, but two new problems:
it is not a very performant solution
regex library is huge and bloats resulting binary
So think twice.
You could write a simple functor to add the hyphens, like this:
#include <iostream>
struct inserter
{
unsigned n = 0u;
void operator()(char c)
{
std::cout << c;
if (++n%3 == 0) std::cout << '-';
}
};
This can be passed to the standard for_each() algorithm:
#include <algorithm>
int main()
{
const std::string s = "thisisateststring";
std::for_each(s.begin(), s.end(), inserter());
std::cout << std::endl;
}
Exercise: extend this class to work with different intervals, output streams, replacement characters and string types (narrow or wide).
I am aware of the fact that there already exist solutions to this that make use of the #include <algorithm> but I was wondering if the following could be a viable way of doing it:
#include <iostream>
using namespace std;
void remove_character_from_result(string& result, char remove){
int size = result.size();
for (int i = 0; i < size; i++){
int pos = result.find(remove);
if(pos == result.npos) break;
else{
if(pos == 0)
result = result.substr(1);
else
result = result.substr(0, pos) + result.substr(pos + 1);
}
}
}
int main() {
string result = "Asad Ahmad";
remove_character_from_result(result, 'A');
cout << result << endl;
return 0;
}
I have tested my code against normal test cases and it does seem to work, I am not sure whether the time complexity would be any better than result.erase(remove(result.begin(), result.end(), 'A'), result.end()); would appreciate if any one could shed some light on this :) PS I apologize for not using references instead of pointers or '*' instead of '->' earlier - this is code that I have written that I am not really proud of..
Your code has an assortment of issues.
#include <iostream>
If you are going to access the std::string class, you need to #include <string>. You shouldn't rely on it being included by <iostream>
using namespace std;
This is a terrible practice, polluting your global namespace with all the names from std. If you want to avoid having to repeat a specific name, prefer the syntax
using std::string;
for example.
void remove_character_from_result(string* result, char remove){
Your code doesn't check for this pointer being null. It would be better to take the parameter by reference.
void remove_character_from_result(string& result, char remove){
int size = (*result).size();
While (*result).size() achieves the end, you should probably use -> to remind yourself you're working with a pointer, and perhaps remember to check it isn't null.
size = result->size();
Here we run into a bug in your program too:
int size = result->size();
size() returns a std::string::size_type which is unsigned and possibly significantly larger that int. Your code stops working as soon as a string is larger than std::numeric_limits<int>::max().
This should either be
auto size = result->size();
or
std::string::size_type size = result->size();
for (int i = 0; i < size; i++){
Again we have the size problem here, but we also have the problem that the size may change during our operations. You should replace both of these lines with:
for (std::string::size_type i = 0; i < result->size(); ++i) {
The next chunk of code:
int pos = (*result).find(remove);
if(pos == (*result).npos) break;
else{
if(pos == 0)
*result = (*result).substr(1);
else
*result = (*result).substr(0, pos) + (*result).substr(pos + 1);
doesn't make a whole lot of sense, and does things the hard way. In the first case, you create a new substring and then copy it into yourself. In the second case you create two temporary substrings which you add together creating a third, and then you copy this into yourself.
It would be better to use erase.
It's also not clear why you have the size-constrained loop in the first place.
You could better replace the whole function with something like this:
#include <iostream>
#include <string>
void remove_character_from_result(std::string& result, char remove) {
for (size_t pos = 0; (pos = result.find(remove, pos)) != result.npos; ) {
size_t end = pos + 1;
while (end < result.size() && result[end] == remove)
++end;
result.erase(pos, end - pos);
}
}
int main() {
std::string s = "hello world";
remove_character_from_result(s, 'l');
std::cout << s << '\n';
}
The time complexity of this for worst case is that it has to remove half the characters, which would be O(NlogN).
You could improve the performance by working from the right rather than the left: Consider removing the character 'a' from 'ababab'. The first erase is followed by copying 5 characters left, the second erase 3 characters and the third erase 1 character. If we worked the other way around, the first erase would be 1 character, the second erase 2 and the third erase 3.
If you really insist on doing this on your own, you probably want to use string's member functions to do the searching. For a (probably tiny) gain in efficiency, you also want to do the searching from the end back to the beginning of the string. I'd also pass the string by reference instead of passing a pointer.
Finally, instead of building a new string out of substrings, you might as well just erase the character you want gone:
void remove_character(string &result, char remove) {
std::string::size_type pos;
while ((pos = result.rfind(remove)) != std::string::npos)
result.erase(pos, 1);
}
If you want to optimize a little (or possibly even quite a bit) more, you can resume the second and subsequent searches from the point where you found the previous instance:
void remove_character(string &result, char remove) {
std::string::size_type pos = std::string::npos;
while ((pos = result.rfind(remove, pos)) != std::string::npos)
result.erase(pos, 1);
}
Hey I'm quite new to programming and I'm having trouble using the isalpha function in my programme. This a part of the code for a palindrome class. What I'm trying to do is remove all the non alphabetic characters from the input. So if the user inputs "Hi, How are you" I need to first count the size of the array of just the letters then in my removeNonLetters subclass, I need to get rid of the non alphabetical characters. Can someone please help me with this. Thank you so much!
#include <iostream>
#include <string>
#include <stdio.h>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <ctype.h>
using namespace std;
class palindrome
{
private:
int only_letters_size;
string input_phrase;
string* only_letters;
public:
string inputPhrase();
string removeNonLetters();
string* new_Array;
int size_new_Array;
};
string palindrome::inputPhrase()
{
cout << "Input phrase: "; //asks the user for the input
getline(cin,input_phrase);
size_new_Array = input_phrase.length(); //creating a dynamic array to store
the input phrase
new_Array = new string[size_new_Array];
int i;
for (i=0; i<size_new_Array; i++)
{
new_Array[i]=input_phrase[i];
}
only_letters_size = 0;
while(new_Array[i])
{
if (isalpha(new_Array[i])) //PROBLEM OCCURS HERE
{
only_letters_size=only_letters_size+1;
}
}
cout << only_letters_size << endl;
return new_Array;
}
string palindrome::removeNonLetters()
{
int j=0;
int str_length = new_Array.length(); //string length
only_letters = new string[only_letters_size];
for (int i=0;i<size_new_Array;i++) //PROBLEM OCCURS HERE AS WELL
{
if (isalpha(new_Array[i]))//a command that checks for characters
{
only_letters[j] = new_Array[i];//word without non alphabetical c
characters is stored to new variable
j++;
}
}
cout << only_letters << endl;
return only_letters;
}
I've found the best way to determine if a string is a palindrome is to walk toward the center from both sides. In your case I would just opt to skip non-alpha characters like so.
bool is_palindrome(string mystring)
{
int start = 0, end = mystring.length() - 1;
while (start < end)
{
// Skip over non-alpha characters
while (!isalpha(mystring[start]))
{
start++;
}
while (!isalpha(mystring[end]))
{
end--;
}
if (tolower(mystring[start]) != tolower(mystring[end]))
{
return false;
}
else
{
start++;
end--;
}
}
return true;
}
If you must save the input first and remove nonalpha characters, I would do it like this.
string remove_non_alpha(string mystring)
{
string ret_string = "";
for (int i = 0; i < mystring.length(); i++)
{
if (isalpha(mystring[i]))
{
ret_string += tolower(mystring[i]);
}
}
return ret_string;
}
And then feed the result into the above function.
Sorry for being hard, but your trying far too much copying around. You can achieve all this with one single loop after retrieving your data and all on one single string object (unless you want to keep the original input for some other purposes):
getline(cin,input_phrase);
std::string::iterator pos = input_phrase.begin();
for(char c : input_phrase)
{
if(isalpha(c))
{
*pos++ = tolower(c);
}
}
input_phrase.erase(pos, input_phrase.end());
After that, your string is ready to use...
Explanation:
std::string::iterator pos = input_phrase.begin();
An iterator something similar than a pointer to the internal data of the string. We keep the position to move the alpha only characters to, skipping the non-alpha ones.
for(char c : input_phrase)
Simply iterating over all characters...
if(isalpha(c))
The essential check, is the current character an alpha one?
*pos++ = tolower(c);
If so, convert it to lower case immediately. Assign it to the current string position, and advance the "pointer" (iterator!).
input_phrase.erase(pos, input_phrase.end());
And at very last, drop the remaining part of the string occupied with surplus characters. You might note that there might be some characters you wanted to keep within, but you copied these to a position more to the left already...
I am learning std::string and I want to :
Input string
Every second letter make Uppercase
Output new string
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
string myStr;
getline(cin,myStr);
if (myStr.begin() != myStr.end())
{
for (auto it = myStr.begin(); it != myStr.end() ; it += 2)
*it = toupper(*it);
}
cout << myStr;
system("pause");
return 0;
}
But after input I am getting error here:
it += 2 leads you out of bounds, if the loop ending condition is it != myStr.end(). Thus dereferencing
*it = toupper(*it);
is undefined behavior.
it += 2 will never give you an exact iterator value of myStr.end() as you have for your loops termination condition.
As for your comment:
So how can i fix it ?
Just keep it simple and understandable, like e.g. using something like
for (size_t i = 0; i < myStr.length() ; ++i) {
if(i % 2) { // Every second letter ...
myStr[i] = toupper(myStr[i]);
}
}
I'm testing a small program in order to create a larger one.
I have a vector of 3 strings:
pass
pass
TEST pass pass
I want to search the vector for the substring "pass" and record how many times "pass" is found in the vector of strings.
So basically I want it to return the number 4 (4 instances of the substring "pass")
Code looks like this
the strings are stored in the vector myV1
if (find(myV1.begin(), myV1.end(), "pass") != myV1.end() )
{
passes++;
}
when I do this it finds "pass" once and ignores the others.
I can't get a loop to work either. It tells me that it found however many instances of the substring "pass" for as many times as i loop through.
Thanks in advance for any advice
In short: here you can find the working code with an online compiler.
All you need is two loops, one for iterating over the vector elements, and one that iterates over each element while counting the desired word occurrence in that particular element. The external loop summarizes it then.
You could use string::find for the internal loop, and the external loop is the regular one with the iterators.
You will need the snippet below to work properly with C++98/03 and C++11 as well.
#include <string>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<string> stringList;
stringList.push_back("pass");
stringList.push_back("pass");
stringList.push_back("Test pass pass");
string searchWord = "pass";
int searchWordSize = searchWord.size();
int count = 0;
for (vector<string>::iterator iter = stringList.begin(); iter != stringList.end(); ++iter) {
// Avoid the overlapping search word. If that is needed, replace
// pos+=searchWordSize with ++pos
for (size_t pos = 0; pos < (*iter).length(); pos+=searchWordSize) {
pos = (*iter).find(searchWord, pos);
if (pos != string::npos)
++count;
else
break;
}
}
cout << "Count: " << count << endl;
return 0;
}
I have built and run the code with the following commands:
g++ main.cpp
./a.out
The output will be 4 as expected.
You could loop vector and use std::string::find to find the occurrence of "pass" in each string.
To count occurrence of a substring correctly, you need to record postion of first occurrence then increment position and continue the search.
int count(const std::string& s, const std::string token = "pass")
{
int n(0);
std::string::size_type pos = s.find(token);
while (pos != std::string::npos)
{
pos = s.find(token, pos + 1);
n++;
}
return n;
}
int main()
{
std::vector<std::string> v = {"pass", "pass", "TEST pass pass"};
int total(0);
for (auto& w : v)
{
total += count(w);
}
std::cout << total << std::endl;
}