This loop is executing more iterations than is expected - c++

I am having problems with the following code. What I expect is for the do-while loop to execute 4 times, once for each line of the text file it is reading in, but in reality is it executing five time, which is resulting in a segfault later in the program. What am I doing wrong here that's causing it to execute the extra iteration? I've tried replacing he do-while with a simple while loop but the result is the same.
int count = 0;
string devices[4];
string line;
ifstream DeviceList;
DeviceList.open("devices/device_list.txt");
do
{
getline(DeviceList, line);
devices[count] = line;
count ++;
} while(!DeviceList.eof());
device_list.txt contains the following:
WirelessAdaptor
GPU
CPU
Display

I think your loop should probably look more like this:
Edit: Added check to ignore empty lines
while (getline(DeviceList, line))
{
if (line.length() > 0)
{
devices[count] = line;
++count;
}
}

eof() doesn't return true until getline consumes the end. It doesn't do this until the getline call after reading the last line. You need to check if eof is true immediately after your getline call:
while(true)
{
getline(DeviceList, line);
if(DeviceList.eof())
break;
}

eof() won't return true until you attempt to read more data than there is left.

Above the line getline(DeviceList, line); insert cout << line.length() << endl; and tell us what happens.

Your text file probably contains a line feed after the last line, so getline reads an empty string before the loop actually ends.

Related

C++ Read in file with only numbers (doubles)

I'm trying to read in a file that should contain only numbers in it. I can successfully read in the entire file if it meets that criteria, but if it so happened to have a letter in it, I need to return false with an error statement.
The problem is I'm finding it hard for my program to error when it finds this character. It can find it no problem, but when it does, it decides to just skip over it.
My code to read in the file and attempt to read in only numbers:
bool compute::Read (ifstream& stream)
{
double value;
string line;
int lineNumber = 1;
if (stream)
{
while (getline(stream, line))
{
lineNumber++;
istringstream strStream(line);
while (strStream >> value)
{
cout << value << endl;
}
}
}
return true;
}
The input file which I use for this is
70.5 61.2 A8 10.2
2
Notice that there is a non-number character in my input file. It should fail and return false at that point.
Currently, all it does is once it hits the "A", it simply returns to the next line, continuing the getline while loop.
Any help with this would be much appreciated.
The stringstream does catch those errors, but you're doing nothing to stop the enclosing loop from continuing when an error is found. You need to tailor your main loop so that it stops when the stringstream finds an error, which you can't do if the stringstream is being reconstructed on each iteration. You should create a for() loop instead and construct the stringstream in the declaration part. And the condition to the loop should be "as long as the stringstream and stream do not catch an error". For example:
for (std::istringstream iss; iss && std::getline(stream, line);)
{
iss.clear();
iss.str(line);
while (iss >> value)
{
std::cout << value << '\n';
}
}
Futhermore, it doesn't look like you need to use std::getline() or std::istringstream if you just want to print each value. Just do:
while (stream >> value) {
std::cout << value << '\n';
}
The above will stop when it finds an invalid character for a double.
You need the code to stop streaming but return false if it hasn't yet reached the end of the "input".
One way, possibly not the most efficient but still one way, to do that is parse a word at a time.
If you read first into a std::string and if it works (so the string is not empty) create an istringstream from that string, or reuse an existing one, and try streaming that into a double value.
If that fails, you have an invalid character.
Of course you can read a line at a time from the file, then split that into words, so that you can output a meaningful error message showing what line the bad text was found.
The issue of reading straight into doubles is that the stream will fail when it reaches end of file.
However it is possible to workaround that too because the reason for failing has an error status which you can check, i.e. you can check if it eofbit is set. Although the f in eofbit stands for "file" it applies to any stream not just files.
Although this method may sound better than reading words into a string first, I prefer that method in normal circumstances because you want to be able to report the error so you'll want to print in the error what was read.

Skip line reading file with string::getline in C++

I'm using the following code to get lines out of a file until "#" is not found. It works, but I don't understand why it gets the same line twice. How do I avoid it, i.e., skip a line after string::find(...) has been called?
do {
getline(file, line);
} while(line.find("#") != string::npos);
Simply put, it doesn't.
You're not performing any checks whatsoever for end-of-input.
It's only after the second iteration, and the second call to getline, that EOF is set on the stream and the .find() operation fails.
The EOF flag is not set when EOF is reached, but after an attempt to read past EOF fails.
Write this:
do {
} while (getline(file, line) && line.find("#") != string::npos);
Oops! Now your loop body is empty, and there are much better ways of writing this.
Perhaps:
while (getline(file, line) && line.find("#") != string::npos)) {}
That's better. =)
You should check if you have reached the end of your file before you check the string it returns.
do {
getline(file, line);
} while(file.good() && line.find("#") != string::npos);

String has a '\n' too much

I have made a Class called FileReader. This is in my read function of this class. It opens a file and reads it. Of course it puts the content of the file in a variable called "content" of my class. It's at the last line.
std::string file_content;
std::string temp;
std::ifstream file;
file.open(filepath,std::ios_base::in);
while(!file.eof()){
temp.clear();
getline(file, temp);
file_content += temp;
file_content += '\n';
}
file_content = file_content.substr(0, file_content.length()-1); //Removes the last new line
file.close();
content = file_content;
The file I am opening has the following content :
"Hello\nWhat's up\nCool".
Of course I didn't write exactly \n in my textfile. But as you can see there is no new line at the end.
My problem is, "content" has, whenever I print it to the screen, a new line at the end. But I removed the last new line... What's wrong?
Classic error, using eof before you read instead of after. This is correct
while (getline(file, temp))
{
file_content += temp;
file_content += '\n';
}
or if you must use eof, remember to use it after getline not before.
for (;;)
{
getline(file, temp);
if (file.eof()) // eof after getline
break;
file_content += temp;
file_content += '\n';
}
It's incredible how many people think that eof can predict whether the next read will have an eof problem. But it doesn't, it tells you that the last read had an eof problem. It's been like this throughout the entire history of C and C++ but it's obviously counter-intuitive because many, many people make this mistake.
eof doesn't get set until you attempt to read past the end of the file. Your loop is iterating four times for three lines; the last iteration reads no data, though.
The more correct way to do this is to change your while loop to while (std::getline(file, temp)); this will terminate the loop when it reaches the end of the file, after the third read.

Missing line of data using Getline with Ifstream

Ok so this is killing me at the moment cause its such a simple part of my program that just doesn't want to work. I'm reading data from a textfile to use in a GA.
The first getline() works perfectly, but the second one doesn't want to write any data into my string. When i cout the string it doesn't show anything.
Here is the code:
ifstream inFile;
inFile.open(fname.c_str());
char pop[20], mut[20];
inFile.getline(pop,20);
cout << pop;
inFile.getline(mut,20);
cout << mut; //this outputs nothing
Thanks for any help in advance.
A sample form my file:
there is no line between them mutation is the line straight after population
Population size: 30
Mutation: 20
Your file's first line is 20 characters long (19+new line) but pop[20] can only contain 19 (because the last one is reserved for the null terminator '\0').
When istream::getline stops because it has extracted 20-1 characters, it doesn't discard the new line delimiter (because it was never read). So the next getline just reads the end of the first line, discarding the new line.
That's why you get nothing in the second string.
Your problem is that the length of your input line exceeds the length of the buffer which must hold it.
The solution is to not use character arrays. This is C++, use std::string!
std::ifstream inFile;
inFile.open(fname.c_str());
std::string pop;
std::getline(inFile, pop);
cout << pop << "\n";
std::string mut;
std::getline(inFile, mut);
cout << mut << "\n";
I think you need to find out what the problem is. Add error checking code to your getline calls, refactor the (simple) code into a (simple) function, with a (simple) unittest. Possibly, your second line is longer than the assumed 20 characters (null-term included!).
For an idea of what I mean, take a look at this snippet.
try something like
while (getline(in,line,'\n')){
//do something with line
}
or try something like
string text;
string temp;
ifstream file;
file.open ("test_text.txt");
while (!file.eof())
{
getline (file, temp);
text.append (temp); // Added this line
}

getline() reads an extra line

ifstream file("file.txt");
if(file.fail())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
cout<<line[l++]<<"\n";
}
}
I am using a two dimensional character array to keep the text (more than one line) read from a file to count the number of lines and words in the file but the problem is that getline always reads an extra line.
Your code as I'm writing this:
ifstream file("file.txt");
if(file.fail())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
cout<<line[l++]<<"\n";
}
}
The first time getline fails, you still increment the line counter and output the (non-existing) line.
Always check for an error.
extra advice: use std::string from the <string> header, and use its getline function.
cheers & hth.
The problem is when you're at the end of the file the test on file will still succeed because you have not yet read past the end of file. So you need to test the return from getline() as well.
Since you need to test the return from getline() to see if it succeeded, you may as well put it right in the while loop:
while (file.getline(line[l], 80))
cout << line[l++] << "\n";
This way you don't need a separate test on file and getline().
This will solve your problem:
ifstream file("file.txt");
if(!file.good())
{
cout<<"Could not open the file";
exit(1);
}
else
{
while(file)
{
file.getline(line[l],80);
if(!file.eof())
cout<<line[l++]<<"\n";
}
}
Its more robust
Does the file end with a newline? If it does, the EOF flag will not be triggered until one extra loop passes. For example, if the file is
abc\n
def\n
Then the loop will be run 3 times, the first time it will get abc, the second time it will get def and the third time it will get nothing. That's probably why you see an additional line.
Try checking the failbit on the stream AFTER the getline.
Only do the cout if file.good() is true. The extra line you're seeing comes from the last call to file.getline() which reads past the end of the file.