Find an item in vector created by own StringTokenizer - c++

I have made my own StringTokenizer class:
class StringTokenizer {
private:
vector<string> tokens; //output
string strIn; //input
public:
StringTokenizer(string strIn) {
this->strIn = strIn;
string str(strIn);
string tmpstr;
stringstream ss(str);
while(ss >> tmpstr)
tokens.push_back(tmpstr);
}
vector<string> getTokens() {
return tokens;
}
}
I have my main function
int main() {
string str = "a b c d";
StringTokenizer st(str);
if(find(st.getTokens().begin(), st.getTokens().end(), item) != st.getTokens().end()) {
cout << "found";
} else {
cout << "not found";
}
}
When function is returning true, everything is working fine. Otherwise program is crashing. What am I doing wrong?

You have undefined behaviour in your code, getTokens() returns a vector by value and you call that twice (once for begin(), once for end()) which means your two iterators don't point to the same container. This just cannot work. And then, you call it a third time to compare the result of find which is also incorrect for the same reason.
You can easily fix this by changing it to be:
auto tokens = st.getTokens();
if(find(tokens.begin(), tokens.end(), item) != tokens.end())
or you can return const vector<string>& from getTokens().

Related

Allocate and return string from function?

What is the best strategy is for returning a modified copy of a string from a function? From my experience in programming in C, I know that it is generally preferable to return a pointer to a large object (array or struct) instead of a copy.
I have the following example, where I pass a string type to a C++ function and return a char *. I was wondering how I should go about returning a string object instead. If I were to return a string, would it be advisable to return a copy or dynamically allocate it and return the pointer?
#include <iostream>
using namespace std;
char *removeSpaces(string sentence)
{
char *newSentence = new char[sentence.size()]; // At most as long as original
int j = 0;
for (int i = 0; i < sentence.size(); i++) {
if (sentence[i] == ' ') {
newSentence[j++] = ' ';
while (sentence[++i] == ' ');
}
newSentence[j++] = sentence[i];
}
return newSentence;
}
int main(void)
{
string sentence;
cout << "Enter a sentence: ";
getline(cin, sentence);
char *sentenceSpacesRemoved = removeSpaces(sentence);
cout << "After removing spaces: ";
cout << sentenceSpacesRemoved << endl;
delete[] sentenceSpacesRemoved;
return 0;
}
Thanks to copy elision, you can just return a stack-allocated string object from a function without any overhead:
std::string getString()
{
std::string str;
return str;
}
Moreover, you can get "guaranteed" elision (since C++17) if you construct the string right in the return expression:
std::string getString()
{
return std::string{};
}
If you don't need a copy at all, however, for your scenario you can just pass the string and remove whitespaces in-place:
void removeSpaces(std::string& sentence)
{
for (auto it = std::cbegin(sentence); it != std::cend(sentence); ++it) {
if (*it == ' ') {
it = sentence.erase(it);
}
}
}

C++ accessing vectors in classes

i am a beginner in C++ and my question is:
why my vector in a class is empty when i try to access that vector elements in another class after i added elements to that vector?
i have a class for example class1 and this class has a vector of type string and a member function which adds elements to the vector with push_back() and another member function which has an argument of type string and it returns true if the argument is in the vector or else it returns false. now if i write another class class2 and it has a vector of type string named valid and a member function named check that it reads a string from input and we have a class1 object that we can access the class1 member function to check if this input is in the vector from class1 but looks like in class2 the vector i had in class1 with elements is empty. what am i doing wrong?
here is code:
class abc{
private:
vector<string> words;
public:
void seta() {
string s;
cout << "word: ";
cin >> s;
words.push_back(s);
}
bool word_check(string a) {
for(string b : words) {
if(b == a) {
return true;
}
}
return false;
}
};
class b{
private:
vector<string> valid;
public:
void check() {
abc mlj;
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
while(w == false) {
cerr << "invalid input, try again: ";
cin.clear();
cin.ignore(INT_MAX, '\n');
cin >> k;
}
valid.push_back(k);
}
};
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check();
}
screenshot of the output
i was expecting that i can access vector elements in class from another class
mlj is a new local object in the check method, and it contains no words. All your words were input in the main function and are stored in vkk. So you need to pass that object to check.
To do that, modify the method to receive a reference
void check(const abc & mlj)
{
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
// ...
valid.push_back(k);
}
Now, this will give you a compiler error, because abc::word_check is a non-const method. Let's also fix that by adding the const specifier to the method definition. While we're at it, let's accept the string as a const reference too, and also use references while iterating over the vector. This avoids unnecessary string copying.
bool word_check(const string& a) const
{
for(const string& b : words) {
if(b == a) {
return true;
}
}
return false;
}
It should be noted that this can also be achieved with std::find which is provided by the standard library in <algorithm>:
bool word_check(const string& a) const
{
return std::find(words.begin(), words.end(), a) != words.end();
}
Let's circle back to your main, and update that to call check correctly:
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check(vkk); // <-- pass vkk here
}
One other thing to note is your loop in check is broken. If w is false, then the loop will never terminate because you never update w again. How about instead you do this:
while ((cin >> k) && !mlj.word_check(k))
{
cerr << "invalid input, try again: ";
cin.ignore(INT_MAX, '\n');
cin >> k;
}
if (cin)
{
valid.push_back(k);
}
This does a couple of things at once...
First, it ensures the stream has actually read a string and not entered some error state (such as end of stream). Under ordinary conditions, reading strings from standard input will not result in error bits being set, so you also don't need cin.clear().
Second, it calls word_check every time around the loop, and only enters the loop body if the check fails. After the loop, we test once again that the stream is good, and if so then it means we read a word and it passed the check.
Make these changes, and you're at least on the way to having a working program. There are other nit-picks I could make, but I may have done too many already so I'll stop! Happy coding!
In the code that you have given as an example, you have created two separate objects of the same class, each of which occupies a different space in memory and is completely independent of each other. Therefore, the mlj object is completely independent from the vkk object and nothing has been inserted in it so far. For your code to work properly I suggest you make the following change to it. That is, give the class abc to the input of class b:
class abc {
private:
vector<string> words;
public:
void seta() {
string s;
cout << "word: ";
cin >> s;
words.push_back(s);
}
bool word_check(string a) {
for (string b : words) {
if (b == a) {
return true;
}
}
return false;
}
};
class b {
private:
vector<string> valid;
public:
void check(abc mlj) {
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
while (w == false) {
cerr << "invalid input, try again: ";
cin.clear();
cin.ignore(INT_MAX, '\n');
cin >> k;
}
valid.push_back(k);
}
};
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check(vkk);
}

Reverse word order in sentence

I'm having difficulty creating a function that reverse the order of the sentence around. I've read many functions on how to recursively reverse the letters around and I have successfully done so, but I do not want to reverse the letters in the words. I want to reverse the placement of the words in the sentence.
Example would be:
This is a sentence.
sentence. a is This
This is my code so far. How do I go from reversing order of letters of the entire sentence to placement order of words in a sentence?
The output of the current code would provide: !dlroW olleH
void reverse(const std::string str)
{
int length = str.size();
if(length > 0)
{
reverse(str.substr(0,length-1));
std::cout << str[0];
}
}
Edit: Additional question. If this was a char array would the logic be different?
Simplify your logic by using a std::istringstream and a helper function. The program below works for me.
#include <sstream>
#include <iostream>
void reverse(std::istringstream& stream)
{
std::string word;
if ( stream >> word )
{
reverse(stream);
std::cout << word << " ";
}
}
void reverse(const std::string str)
{
std::istringstream stream(str);
reverse(stream);
std::cout << std::endl;
}
int main(int argc, char** argv)
{
reverse(argv[1]);
return 0;
}
// Pass string which comes after space
// reverse("This is a sentence.")
// reverse("is a sentence.")
// reverse("a sentence.")
// reverse("sentence.")
// will not find space
// start print only word in that function
void reverse(const std::string str)
{
int pos = str.find_first_of(" ");
if (pos == string::npos) // exit condition
{
string str1 = str.substr(0, pos);
cout << str1.c_str() << " " ;
return;
}
reverse(str.substr(pos+1));
cout << str.substr(0, pos).c_str() << " ";
}
Simple to understand:
void reverse(const std::string str)
{
int pos = str.find_first_of(" ");
if (pos != string::npos) // exit condition
{
reverse(str.substr(pos + 1));
}
cout << str.substr(0, pos).c_str() << " ";
}
std::vector<std::string> splitString(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> tokens;
while (getline(ss, item, delim)) {
tokens.push_back(item);
}
return tokens;
}
void reverseString(const std::string& string) {
std::vector<std::string> words = splitString(string, ' ');
auto end = words.rend();
for (auto it = words.rbegin(); it <= end; it++) {
std::cout << *it << std::endl;
}
}
reverseString("This is a sentence.");
You can split input and print them in inverse order
Or if you want to use recursive structure just move the cout after calling a function like this:
void reverse(const std::string str)
{
std::stringstream ss(str);
std::string firstWord, rest;
if(ss >> firstWord)
{
getline(ss , rest);
reverse(rest);
std::cout << firstWord << " ";
}
}
I am not a C++ programmer, but I would create another array (tempWord[ ]) to store individual word.
Scan each word and store them into tempWord array. In your case, the words are separated by space, so:
a.get the index of the next space,
b substring to the index of the next space and
c. you should get {"This", "is", "a", "sentence."}
Add them up again reversely:
a. loop index i from "tempWord.length -1" to "0"
b. new String = tempWord[i]+" ";
print out result.

C++ Extract int from string using stringstream

I am trying to write a short line that gets a string using getline and checks it for an int using stringstream. I am having trouble with how to check if the part of the string being checked is an int. I've looked up how to do this, but most seem to throw exceptions - I need it to keep going until it hits an int.
Later I will adjust to account for a string that doesn't contain any ints, but for now any ideas on how to get past this part?
(For now, I'm just inputting a test string rather than use getline each time.)
int main() {
std::stringstream ss;
std::string input = "a b c 4 e";
ss.str("");
ss.clear();
ss << input;
int found;
std::string temp = "";
while(!ss.eof()) {
ss >> temp;
// if temp not an int
ss >> temp; // keep iterating
} else {
found = std::stoi(temp); // convert to int
}
}
std::cout << found << std::endl;
return 0;
}
You could make of the validity of stringstream to int conversion:
int main() {
std::stringstream ss;
std::string input = "a b c 4 e";
ss << input;
int found;
std::string temp;
while(std::getline(ss, temp,' ')) {
if(std::stringstream(temp)>>found)
{
std::cout<<found<<std::endl;
}
}
return 0;
}
While your question states that you wish to
get a string using getline and checks it for an int
using stringstream, it's worth noting that you don't need stringstream at all. You only use stringstreams when you want to do parsing and rudimentary string conversions.
A better idea would be to use functions defined by std::string to find if the string contains numbers as follows:
#include <iostream>
#include <string>
int main() {
std::string input = "a b c 4 e 9879";//I added some more extra characters to prove my point.
std::string numbers = "0123456789";
std::size_t found = input.find_first_of(numbers.c_str());
while (found != std::string::npos) {
std::cout << found << std::endl;
found = input.find_first_of(numbers.c_str(), found+1);
}
return 0;
}
And then perform the conversions.
Why use this? Think about happens if you use a stringstream object on something like the following:
"abcdef123ghij"
which will simply be parsed and stored as a regular string.
Exceptions should not scary you.
int foundVal;
found = false;
while(!found || !ss.eof()) {
try
{
foundVal = std::stoi(temp); //try to convert
found = true;
}
catch(std::exception& e)
{
ss >> temp; // keep iterating
}
}
if(found)
std::cout << foundVal << std::endl;
else
std::cout << "No integers found" << std::endl;

vector in function - how to do return

I've got a function that should read from file line by line, the reading stops when a line does not begin with '>' or ' '. It should store the lines in vector and return it. This is code:
#include <cstdlib>
#include <iostream>
#include <string>
#include <stdio.h>
#include <fstream>
#include <vector>
using namespace std;
string getseq(char * db_file) // gets sequences from file
{
string seqdb;
vector<string> seqs;
ifstream ifs(db_file);
string line;
//vector<char> seqs[size/3];
while(ifs.good())
{
getline(ifs, seqdb);
if (seqdb[0] != '>' & seqdb[0]!=' ')
{
seqs.push_back(seqdb);
}
}
ifs.close();
//return seqs;
//return seqs;
}
int main(int argc, char * argv[1])
{
cout << "Sequences: \n" << getseq(argv[1]) << endl;
return 0;
}
Compiler (g++) returns:
fasta_parser.cpp: In function ‘std::string getseq(char*)’:
fasta_parser.cpp:32: error: conversion from ‘std::vector<std::basic_string<char, `std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >’ to non-scalar type ‘std::string’ requested`
Anyone has any idea?
Edit:
As Skurmendel ask, I am adding whole code because of memory security violation after
executing compiled code:
#include <cstdlib>
#include <iostream>
#include <string>
#include <stdio.h>
#include <fstream>
#include <vector>
using namespace std;
vector<string> getseq(char * db_file) // pobiera sekwencje z pliku
{
string seqdb;
vector<string> seqs;
ifstream ifs(db_file);
string line;
//vector<char> seqs[size/3];
while(ifs.good())
{
getline(ifs, seqdb);
if (seqdb[0] != '>' & seqdb[0]!=' ')
{
seqs.push_back(seqdb);
}
}
ifs.close();
return seqs;
}
int main(int argc, char * argv[1])
{
vector<string> seqs; // Holds our strings.
getseq(argv[1]); // We don't return anything.
// This is just a matter of taste, we create an alias for the vector<string> iterator type.
typedef vector<string>::iterator string_iter;
// Print prelude.
cout << "Sekwencje: \n";
// Loop till we hit the end of the vector.
for (string_iter i = seqs.begin(); i != seqs.end(); i++)
{
cout << *i << " "; // Do processing, add endlines, commas here etc.
}
cout << endl;
}
If I understood you, your getseq() should return a vector of strings. Therefore you should change
string getseq(char * db_file)
to
vector<string> getseq(char * db_file)
And if you want to print it on main() you should do it in a loop.
int main() {
vector<string> str_vec = getseq(argv[1]);
for(vector<string>::iterator it = str_vec.begin(); it != str_vec.end(); it++) {
cout << *it << endl;
}
}
Your function getseq is declared to return std::string but you are trying to return value of another type - std::vector - therefore you got that compiler error. You need to return variable of type std::string (created by concatenating elements of your vector).
Your function could look like this:
string getseq(char* db_file)
{
string strSeqs;
vector<string> seqs;
... // fill the vector; manipulate with ifstream
for(vector<string>::iterator it = seqs.begin(); it != seqs.end(); ++it)
{
strSeqs += *it;
}
return strSeqs;
}
Note: string you are returning from a function can be quite big object and returning it by value can be expensive as what is actually returned in this case is a copy of that object (constructed by invoking copy constructor). It would be more efficient if your string is declared as out parameter which you just fill inside the function:
void getseq(char* db_file, string& strSeqs);
string strSeqs;
getseq(argv[1], strSeqs);
cout << strSeqs << endl;
you try to return a vector and your method must return string.
maybe you have to change a signature of method to
vector<string> getseq(char * db_file)
Well, you are trying to return a vector as strings. This won't work because they are different types and have no conversion defined from one to the other. Your function has the return type string.
Solution 1
In your case you could append the lines to a string instead of adding them to a vector? You are using the result as a string anyhow.
You could change seqs to string and append data to it with the += operator.
Solution 2
You could also change the return type to vector<string> but you would need to loop over the items and print them instead in your main.
vector<string> getseq(char * db_file)
{
...
return seqs;
}
Caveat Lector: this will copy all the items. If you want to avoid this pass the vector as a reference to the function and add to it.
Looping is quite easy using iterators:
// Get the strings as a vector.
vector<string> seqs = getseq(argv[1]);
// This is just a matter of taste, we create an alias for the vector<string> iterator type.
typedef vector<string>:iterator_t string_iter;
// Loop till we hit the end of the vector.
for (string_iter i = seqs.begin(); i != seqs.end(); i++)
{
cout << *i; // you could add endlines, commas here etc.
}
If you want to avoid copying a vector and all the strings make getseq take a reference to a vector<string>.
void getseq(char * db_file, vector<string> &seqs)
{
...
// vector<string> seqs; this line is not needed anymore.
...
// we don't need to return anything anymore
}
You would then need to create the vector<string> in your main instead, making my above code:
// Get the strings as a vector.
vector<string> seqs; // Holds our strings.
getseq(argv[1], seqs); // We don't return anything.
// This is just a matter of taste, we create an alias for the vector<string> iterator type.
typedef vector<string>:iterator_t string_iter;
// Print prelude.
cout << "Sekwencje: \n";
// Loop till we hit the end of the vector.
for (string_iter i = seqs.begin(); i != seqs.end(); i++)
{
cout << *i << " "; // Do processing, add endlines, commas here etc.
}
cout << endl;
Edit after comments
int main(int argc, char * argv[1])
{
// This is what you need, sorry for the confusion.
// This copies the vector returned to seqs
vector<string> seqs = getseq(argv[1]);
// This is just a matter of taste, we create an alias for the vector<string> iterator type.
typedef vector<string>::iterator string_iter;
// Print prelude.
cout << "Sekwencje: \n";
// Loop till we hit the end of the vector.
for (string_iter i = seqs.begin(); i != seqs.end(); i++)
{
cout << *i << " "; // Do processing, add endlines, commas here etc.
}
cout << endl;
}