Never reaching eof using ifstream (cpp) - c++

I am reading text from a text file, but I never reach eof, which results in an
endless loop.
Here's what I wrote
static ifstream inF;
inF.open(file,ifstream::in);
cin.rdbuf(inF.rdbuf());
while (inF.good() && !inF.eof())
{
addStudent(students);
}
if (inF.is_open())
{
inF.close();
inF.clear();
}
Every Iteration of the loop I call addStudents, which handles only one line. That works fine for me. Basically I rad lines in the form of D 98 76.5 66 45 (Possibly) 12000
here's the code:
static void addStudent(vector<Student*> students)
{
char institution;
unsigned short id;
double gAverage, pGrade, salary;
cin >> institution;
switch (institution)
{
case ALICE:
cin >> id >> gAverage >> salary;
students.push_back(new Student(institution,id,gAverage,salary));
return;
case BOB:
cin >> id >> gAverage >> salary;
students.push_back(new Student(institution,id,gAverage,salary));
return;
case COLIN:
cin >> id >> gAverage >> pGrade >> salary;
students.push_back(new CollegeStudent(institution,id,gAverage,pGrade,salary));
return;
case DANNY:
cin >> id >> gAverage >> pGrade >> salary;
students.push_back(new CollegeStudent(institution,id,gAverage,pGrade,salary));
return;
}
}
When I get to the end of the file the loop keeps running, and addStudents (which returns void) does nothing. Any Ideas why?
Thanks!

Your file stream may share it's stream buffer with cin, but it doesn't share it's flags. So when you read using cin's operator>>, and it the reaches end of the file, cin sets it's own eof flag, but it doesn't set the flag in your ifstream(how could it? it has no knowledge of it).
This is an awfully silly way to read a file, why are you doing it like that? Why don't you just pass an istream reference into your addStudent function and read from that?

Related

C++ how to check if cin does not equal something in a while loop condition?

I am trying to learn file i/o in c++. I have a bunch of variables like kill, death, assist, result, notes and I want to enter them into a text file. I have seen tutorials where they use cin inside a while loop like
while ( cin >> kill >> death >> assist ) {
file << kill << death << assist ; }
but I want the user to type in "done" when they are done entering their input.
I know in python you can do something like
while (input != "done" ):
//code//
can I do something similar?
while ( cin >> () != "done" ) {
cin >> kill >> death >> assist >> notes ;
file << kill << death << assist << notes ; }
You can have multiple conditions using the logical AND and OR operators.
You can also use std::getline to read a line, and then use an input string stream to parse the input.
For example something like:
std::string input;
while (std::getline(cin, input) && input != "done")
{
std::istringstream is(input);
is >> kill >> death >> assist;
...
}
The expression in the condition reads a while line, checks that there was no errors or end-of-file when reading, and then check that the input was not "done".

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;

reading file into vectors, what's wrong with my input function?

I have an assignment to create a record management system for a class project. When adding records I would like to first read into a vector the contents of my record file currently then perform additions to the file finally outputting back to the record file. However, I'm having a hard time wrapping my mind around how to structure this. I am currently using a dynamic array to store the data but when I try to put it into the vector I it won't let me because it's a pointer. I feel like I'm approaching this entirely wrong and could use some assistance. Here is my input function:
void student::input(istream& inF, student* stud, vector<student>& vect, int size)
{
//local variables
string first, middle, last, addressNum, addressStreet,
phone, gender, email, emContactFirst, emContactLast;
int ID, age;
string ph, emPhone;
while (inF)
{
for (int index = 0; index < size; index++){
inF >> first >> last >> middle;
stud->setName(first, last, middle);
inF >> ID;
stud->setId(ID);
inF >> age;
stud->setAge(age);
inF >> phone;
stud->setPhone(phone);
inF >> addressNum >> addressStreet;
stud->setAddress(addressNum, addressStreet);
inF >> gender;
stud->setGender(gender);
inF >> email;
stud->setEmail(email);
inF >> emPhone;
stud->setEmPhone(emPhone);
inF >> emContactFirst >> emContactLast;
stud->setEmContact(emContactFirst, emContactLast);
inF >> stud->gpa >> stud->hobbies >> stud->major
>> stud->probation;
if (inF.eof())
break;
else
stud++;
vect.push_back(stud);
}
}
}
Problems I see:
You are using while (inF) to break the loop. See Why is iostream::eof inside a loop condition considered wrong?.
You are using one pointer, stud to read all the values and storing the same pointer multiple times in vect. First of all, the compiler should produce an error. You cannot add a pointer to a vector of objects.
It's not clear why the function needs stud as an argument. You can just as easily use a local object in the function. Like this:
for (int index = 0; index < size; index++){
student stud;
if ( !(inF >> first >> last >> middle) )
{
// Deal with error.
}
stud.setName(first, last, middle);
...
}
It's better to check whether the calls to inF >> ... assigned anything successfully and not assume that it succeeded. Instead of:
inF >> first >> last >> middle;
use
if ( !(inF >> first >> last >> middle) )
{
// Deal with error.
}
I suggest changing all such calls.

How to use "gets" function in C++ after previous input?

I tried to input data with gets() function, but whenever program execution get to the the lien with the gets, it ignores it.
When I use gets() without previous data input, it runs properly. But when I use it after data input the problem happens.
Here's the code where it is used after previous data input (so in execution I can't input data to string):
int main() {
char str[255];
int a = 0;
cin >> a;
if(a == 1) {
gets(str);
cout << "\n" << str << endl;
}
}
How could I fix this?
NB: the same happens with cin.getline
After
cin >>a
when you input a and enter, there is also a \n character left by cin, therefore, when you use cin.getline() or gets(str) it will read that newline character.
try the following:
cin >>a;
cin.ignore(); //^^this is necessary
if(a==1){
gets(str);
}
You'd better use C++ way of reading input:
cin >> a;
cin.ignore();
string str;
if (a == 1)
{
getline(cin, str);
}

Read from a file. C++

SO when my program starts, it attempts to read a list of products from a file. but if the file does not exist it displays an error and continue on. the problem im having is when it displays the error, it doesnt continue on to the do while loop
ifstream input;
input.open("data.txt");
if (input.fail())
{
cout << "\n Data file not found \n";
}
ListItemType data;
input >> data.productname;
while(( !input.eof()))
{
input >> data.category;
input >> data.productprice;
addproduct(head, data);
input >> data.productname;
}
input.close();
It's not identical functionality, but it's generally better to move towards something like:
if (std::ifstream input("data.txt"))
{
ListItemType data;
while (input >> data.productname >> data.category >> data.productprice >> data.productname)
addproduct(head, data);
if (!input.eof())
std::cerr << "Error parsing input file.\n";
}
else
cout << "\n Data file not found \n";
If you structure your if/else clauses as above, whatever happens it will continue to the following code as you'd like.
Note that the code above checks for a problem after each input operation. Your code tries to read data.productprice even if reading data.category failed. It's kind of weird you're reading productname twice, and I'm assuming you can call addproduct after the I/O - if not you'll need a while loop like:
while (input >> data.productname >> data.category >> data.productprice)
{
addproduct(head, data);
if (!(input >> data.productname))
break;
}