Overloading operator >> and << so that it accepts user-defined object - c++

I've got an object (of class myObj) that contains multiple strings (a string pointer). I want to overload the >>operator so that I can read in multiple strings at a time.
This overloaded operator functions accept statements like:
cin >> str;
cout << str;
The only problem is that when I fill in a series of strings, it seems that only the first string gets correctly processed in the stream.
To insert:
istream &operator>>(istream &is, myObj &obj)
{
std::string line;
while (true)
{
std::getline(is, line);
if (not is)
break;
obj.add(line);
is >> line;
}
return is;
}
To extract
ostream &operator<<(ostream &os, myObj const &obj)
{
for(size_t idx = 0; idx != obj.size(); ++idx)
os << obj[idx];
return os;
}
The code compiles fine but when I cout the object only the first string is printed and the other strings are omitted.
So when I provide cin with:
Hi
Stack
Exchange
only Hi will be displayed.
Does anyone know why this happens?
Thanks in advance!
P.S I am new to Stack Exchange and I am working hard to formulate the problems as best as I can :)

Your loop will work like that:
std::getline(is, line);
Extracts and stores "Hi" in line, extracts the newline
obj.add(line);
Adds "Hi" to obj
is >> line;
Extracts and stores "Stack" in line, does not extracts the following newline
std::getline(is, line);
Extracts and stores an empty string in line, because next read char is a newline
obj.add(line);
Adds empty string "" to obj
is >> line;
Extracts and stores "Exchange" in line
std::getline(is, line);
Extracts nothing (end of input stream)
if (not is)
break;
Then the stream is at end, your loop exits.
Conclusion : you stored only Hi and an empty string

is >> line puts a leading newline in the stream which is being read by std::getline() during the following loop. Because std::getline() stops input when it finds a newline, this reads an empty string into line and thus the stream is put into an error state which the loop responds to by breaking out of it.
There doesn't seem to be a need for that last read. You can remove it.
Also, this is a more idiomatic way to loop with input. This way you don't have to check the state of the stream within the loop body:
for (std::string line; std::getline(is, line); )
{
obj.add(line);
}
And since there's only one token per line, you can use a formmatted extractor:
for (std::string word; is >> word; )
{
obj.add(word);
}

Related

Simple casting conversion on C++

I'm doing an exercise for the college and I have to compare a string added including the header <string>, and a character.
I have a text file with a few lines of data from a census, like
Alabama AL 4849377 Alaska AK 736732 Arizona AZ 6731484
I want to read the state name of each line with a string variable, but the comparison is the only thing that I am asking for, because is where I have the error.
I have this fragment of code:
struct Census{
string name;
int population, code;
};
struct States{
Census state;
};
typedef States Vector[US_STATES];
void loadCensus(ifstream & census, Vector stats){
int i=0;
string readData;
string line;
while (getline(census, line)) {
stringstream linestream(line);
while (linestream >> readData) {
if (linestream >> stats[i].state.name >>
stats[i].state.code >>
stats[i].state.population)
{
std::cerr << "Bad input on line " << i << ": " << line << std::endl;
}
stats[i].state.name=readData;
stats[i].state.code=readData;
stats[i].state.population=readData;
i++;
}
}
}
How I should convert readData to an integer to assign stats[i].state.population=readData?
I get an error in line 17 in the linestream >> readData.
You want to use the getline() function instead.
I think ita a member function of ifstream or either compare the not readData to a string ("\n") - double quotation. Or put the read data into a string and check if the sting contains a '\n'.
census >> readData will read the next word (any group of non-whitespace characters) from the input. In order to do this, it will discard all whitespace on its hunt for the next word. '\n' is whitespace, so you will never read it with the >> operator without playing games you probably don't want to play.
Instead of >>, use std::getline to read a line and then use a std::stringstream to break the line up into words.
std::string line;
while (std::getline(census, line)) {
std::stringgstream linestream(line);
while (linestream >> readData) {
statistics.state[i]=readData;
i++;
}
}
But...
I do not believe statistics.state[i]=readData; does quite what you want to do. You probably want something more like:
std::string line;
while (std::getline(census, line)) {
std::stringstream linestream(line);
if (!(linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population))
{
std::cerr << "Bad input on line " << i << ": " << line << std::endl;
}
i++;
}
In this state becomes an array or vector of objects that probably looks something like
struct statestats
{
std::string name;
std::string abbreviation;
int population;
};
Breaking it down line by line
std::stringstream linestream(line);
Makes a stringstream. A string stream is a stream like cin and cout or a fstream, but it contains a string. The main use is to buffer and build strings with the same syntax you would use on another stream. In this case we are use it to split up the line into words.
if (linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population)
Needs to be handled in a few parts in a few parts. Over all it is an abbreviation of
if (linestream >> statistics.state[i].name &&
linestream >> statistics.state[i].abbreviation &&
linestream >> statistics.state[i].population)
Each stage of which reads from the linestream into a variable.
Next, the >> operator returns the stream being read, and this is used two ways in the example. The first allows chaining. The output of one >> is used as the input of the next, so if you look at >> as you would a function (and it is a function. See Stream extraction and insertion for more) you can think about it looking something like this:
linestream.read(statistics.state[i].name).read(statistics.state[i].abbreviation).read(statistics.state[i].population)
The >> syntax just makes it easier.
The next advantage you get from returning the stream is the stream can be tested to see if the stream is still good. It has a boolean operator that will return true if the stream is in a good state and can be used.
if(linestream)
{
good
}
else
{
bad
}
will enter good if the stream is open, has not reached the end of the stream, and has had no troubles reading or writing data.
Going back to our example
if (linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population)
Will enter the body of the if statement if the stream successfully read all three values from the stream. Which is not what we want. Ooops. I've corrected the above code already.
if (!(linestream >> statistics.state[i].name >>
statistics.state[i].abbreviation >>
statistics.state[i].population))
will enter the body of the if if at least one value was not read for any reason and print out an error message. Normally when there is an error you will need to clear the error before continuing, but in this case we've use the whole stream and are about to discard it.
Assuming no error occurred all of the data from this line has been read and there is no need to
stats[i].state.name=readData;
stats[i].state.code=readData;
stats[i].state.population=readData;

C++: Why won't getline() print the last word of the input string?

i'm trying to get my program to read a string and then output each word on an individual line. When I call this function it is not printing the last word of the sentence. I have not been able to find an answer to this problem.
For example:
Input:
Hello there my friend
Output:
Hello
there
my
Here is my code:
istream& operator >> (istream& in, FlexString& input) {
std::string content;
while (std::getline (in,content,' ')) {
cout << content << endl;
}
return in;
}
I'm new to C++ so this may be dumb, but I tried adding another cout call to print content on the next line after the while loop but it won't print it for some reason.
getline didn't skip the last word. It's still waiting for you to finish it. You selected the space character (' ') as the delimiter, so getline is going to read until if finds a space (not a tab or a newline), or until the input stream ends. Your loop isn't going to stop at the end of the line either, like you seem to be expecting. It is going to keep reading until the stream ends.
If you want to read a single line, and then separate the line word by word, then just call getline once, with the \n delimiter (which is the default). Then use an istringstream to separate the resulting string word by word.
std::string line;
std::getline(in, line);
std::istringstreaam iss(line);
std::string content;
while (iss >> content)
std::cout << content << std::endl;

Reading in file with delimiter

How do I read in lines from a file and assign specific segments of that line to the information in structs? And how can I stop at a blank line, then continue again until end of file is reached?
Background: I am building a program that will take an input file, read in information, and use double hashing for that information to be put in the correct index of the hashtable.
Suppose I have the struct:
struct Data
{
string city;
string state;
string zipCode;
};
But the lines in the file are in the following format:
20
85086,Phoenix,Arizona
56065,Minneapolis,Minnesota
85281
56065
Sorry but I still cannot seem to figure this out. I am having a really hard time reading in the file. The first line is basically the size of the hash table to be constructed. The next blank line should be ignored. Then the next two lines are information that should go into the struct and be hashed into the hash table. Then another blank line should be ignored. And finally, the last two lines are input that need to be matched to see if they exist in the hash table or not. So in this case, 85281 is not found. While 56065 is found.
As the other two answers point out you have to use std::getline, but this is how I would do it:
if (std::getline(is, zipcode, ',') &&
std::getline(is, city, ',') &&
std::getline(is, state))
{
d.zipCode = std::stoi(zipcode);
}
The only real change I made is that I encased the extractions within an if statement so you can check if these reads succeeded. Moreover, in order for this to be done easily (you wouldn't want to type the above out for every Data object), you can put this inside a function.
You can overload the >> operator for the Data class like so:
std::istream& operator>>(std::istream& is, Data& d)
{
std::string zipcode;
if (std::getline(is, zipcode, ',') &&
std::getline(is, d.city, ',') &&
std::getline(is, d.state))
{
d.zipCode = std::stoi(zipcode);
}
return is;
}
Now it becomes as simple as doing:
Data d;
if (std::cin >> d)
{
std::cout << "Yes! It worked!";
}
You can use a getline function from <string> like this:
string str; // This will store your tokens
ifstream file("data.txt");
while (getline(file, str, ',') // You can have a different delimiter
{
// Process your data
}
You can also use stringstream:
stringstream ss(line); // Line is from your input data file
while (ss >> str) // str is to store your token
{
// Process your data here
}
It's just a hint. Hope it helps you.
All you need is function std::getline
For example
std::string s;
std::getline( YourFileStream, s, ',' );
To convert a string to int you can use function std::stoi
Or you can read a whole line and then use std::istringstream to extract each data with the same function std::getline. For example
Data d = {};
std::string line;
std::getline( YourFileStream, line );
std::istringstream is( line );
std::string zipCode;
std::getline( is, zipCode, ',' );
d.zipCode = std::stoi( zipCode );
std::getline( is, d.city, ',' );
std::getline( is, d.state, ',' );

c++ string manipulation reversal

I am currently doing c++ and am going through how to take in an sentence through a string and reverse the words (This is a word......word a is This etc)
I have looked at this method:
static string reverseWords(string const& instr)
{
istringstream iss(instr);
string outstr;
string word;
iss >> outstr;
while (iss >> word)
{
outstr = word + ' ' + outstr;
}
return outstr;
}
int main()
{
string s;
cout << "Enter sentence: ";
getline(cin, s);
string sret = reverseWords(s);
cout << reverseWords(s) << endl;
return 0;
}
I have gone through the function and kind of understand but I am a bit confused as to EXACTLY what is going on at
iss >> outstr;
while (iss >> word)
{
outstr = word + ' ' + outstr;
}
return outstr;
Can anybody explain to me the exact process that is happening that enables the words to get reversed?
Thank you very much
iss is an istringstream, and istringstreams are istreams.
As an istream, iss has the operator>>, which reads into strings from its string buffer in a whitespace delimeted manner. That is to say, it reads one whitespace separated token at a time.
So, given the string "This is a word", the first thing it would read is "This". The next thing it would read would be "is", then "a", then "word". Then it would fail. If it fails, that puts iss into a state such that, if you test it as a bool, it evaluates as false.
So the while loop will read one word at a time. If the read succeeds, then the body of the loop appends the word to the beginning of outstr. If it fails, the loop ends.
iss is a stream, and the >> is the extraction operator. If you look upon the stream as a continuous line of data, the extraction operator removes some data from this stream.
The while loop keep extracting words from the stream until it is empty (or as long as the stream is good one might say). The inside of the loop is used to add the newly extracted word to the end of the outstr
Look up information about c++ streams to learn more.
The instruction:
istringstream iss(instr);
allows instr to be parsed when the operator>> is used, separating words thourgh a whitespace character. Each time the operator >> is used it makes iss point to the next word of the phrase stored by instr.
iss >> outstr; // gets the very first word of the phrase
while (iss >> word) // loop to get the rest of the words, one by one
{
outstr = word + ' ' + outstr; // and store the most recent word before the previous one, therefore reversing the string!
}
return outstr;
So the first word retrieved in the phrase is actually stored in the last position of the output string. And then all the subsequent words read from the original string will be put before the previous word read.

Using C++ ifstream extraction operator>> to read formatted data from a file

As my learning, I am trying to use c++ ifstream and its operator>> to read data from a text file using code below. The text file outdummy.txt has following contents:
just dummy
Hello ofstream
555
My questions is how to read char data present in the file into a char array or string. How to do this using the ifstream::operator>> in code below.
#include <iostream>
#include <fstream>
int main()
{
int a;
string s;
char buf[100];
ifstream in("outdummy.txt",ios_base::in);
in.operator>>(a); //How to read integer? How to read the string data.??
cout << a;
in.close();
getchar();
return 0;
}
If you want to use formatted input, you have to know in advance what data to expect and read it into variables of the according data type. For example, if you know that the number is always the fifth token, as in your example, you could do this:
std::string s1, s2, s3, s4;
int n;
std::ifstream in("outdummy.txt");
if (in >> s1 >> s2 >> s3 >> s4 >> n)
{
std::cout << "We read the number " << n << std::endl;
}
On the other hand, if you know that the number is always on the third line, by itself:
std::string line;
std::getline(in, line); // have line 1
std::getline(in, line); // have line 2
std::getline(in, line); // have line 3
std::istringstream iss(line);
if (iss >> n)
{
std::cout << "We read the number " << n << std::endl;
}
As you can see, to read a token as a string, you just stream it into a std::string. It's important to remember that the formatted input operator works token by token, and tokens are separated by whitespace (spaces, tabs, newlines). The usual fundamental choice to make is whether you process a file entirely in tokens (first version), or line by line (second version). For line-by-line processing, you use getline first to read one line into a string, and then use a string stream to tokenize the string.
A word about validation: You cannot know whether a formatted extraction will actually succeed, because that depends on the input data. Therefore, you should always check whether an input operation succeeded, and abort parsing if it doesn't, because in case of a failure your variables won't contain the correct data, but you have no way of knowing that later. So always say it like this:
if (in >> v) { /* ... */ } // v is some suitable variable
else { /* could not read into v */ }
if (std::getline(in, line)) { /* process line */ }
else { /* error, no line! */ }
The latter construction is usually used in a while loop, to read an entire file line by line:
while (std::getline(in, line)) { /* process line */ }
ifstream has ios_base::in by default. You don't need to specify it.
operator>> can be invoked directly as an operator: in >> a.
Reading strings is the same: in >> s, but the caveat is that it is whitespace-delimited, so it will read "just" by itself, without "dummy".
If you want to read complete lines, use std::getline(in, s).
Since you have elected to use C-strings, you can use the getline method of your ifstream object (not std::getline() which works with std::strings), which will allow you to specify the C-string and a maximum size for the buffer.
Based on what you had, and adding an additional buffer for the second line:
char buf[100];
char buf2[100];
in.getline(buf,sizeof(buf));
in.getline(buf2,sizeof(buf2));
in >> a;
However, as the other poster has proposed, try using the std::string and its methods, it will make your life easier.
You can read file contents and use a Finite State Machine for parsing.
Example:
void Parse(const char* buffer, size_t length);
size_t GetBufferSize();
size_t bufferSize = GetBufferSize();
char* buffer = new char[bufferSize];
std::ifstream in("input.txt");
while(in.getline(buffer, bufferSize)) {
Parse(buffer, in.gcount());
}
Alternatively, you can use a tool like Flex to write your parser.