I am using the standard C++ fstreams library and I am wondering what is the right way to use it. By experience I sort of figured out a small usage protocol, but I am not really sure about it. For the sake of simplicity let's assume that I just want to read a file, e.g., to filter its content and put it on another file. My routine is roughly as follows:
I declare a local istream i("filename") variable to open the file;
I check either i.good() or i.is_open() to handle the case where something went bad when opening, e.g., because the file does not exist; after, I assume that the file exists and that i is ok;
I call i.peek() and then again i.good() or i.eof() to rule out the case where the file is empty; after, I assume that I have actually something to read;
I use >> or whatever to read the file's content, and eof() to check that I am over;
I do not explicitly close the file - I rely on RAII and keep my methods as short and coherent as I can.
Is it a sane (correct, minimal) routine? In the negative case, how would you fix it? Please note that I am not considering races - synchronization is a different affair.
I would eliminate the peek/good/eof (your third step). Simply attempt to read your data, and check whether the attempted read succeeded or failed. Likewise, in the fourth step, just check whether your attempted read succeeded or not.
Typical code would be something like:
std::ifstream i("whatever");
if (!i)
error("opening file");
while (i >> your_data)
process(your_data);
if (!i.eof())
// reading failed before end of file
It's simpler than you have described. The first two steps are fine (but the second is not necessary if you follow the rest of my advice). Then you should attempt extraction, but use the extraction as the condition of a loop or if statement. If, for example, the file is formatted as a series of lines (or other delimited sequences) all of the same format, you could do:
std::string line;
while (std::getline(i, line)) {
// Parse line
}
The body of the loop will only execute if the line extraction works. Of course, you will need to check the validity of the line inside the loop.
If you have a certain series of extractions or other operations to do on the stream, you can place them in an if condition like so:
if (i >> some_string &&
i.get() == '-' &&
i >> some_int) {
// Use some_string and some_int
}
If this first extraction fails, the i.ignore() not execute due to short-circuit evaluation of &&. The body of the if statement will only execute if both extractions succeed. If you have two extractions together, you can of course chain them:
if (i >> some_string >> some_int) {
// Use some_string and some_int
}
The second extraction in the chain will not occur if the first one fails. A failed extraction puts the stream in a state in which all following extractions also fail automatically.
For this reason, it's also fine to place the stream operations outside of the if condition and then check the state of the stream:
i >> some_string >> some_int;
if (i) {
// Use some_string and some_int
}
With both of these methods, you don't have to check for certain problems with the stream. Checking the stream for eof() doesn't necessarily mean that the next read will fail. A common case is when people use the following incorrect extraction loop:
// DO NOT DO THIS
while (!i.eof()) {
std::getline(i, line)
// Do something with line
}
Most text files end with an extra new line at the end that text editors hide from you. When you're reading lines from the text file, for the last iteration you haven't yet hit the end of file because there's still a \n to read. So the loop continues, attempts to extract the next line which doesn't exist and screws up. People often observe this as "reading the last line of the file twice".
Related
I've found many posts saying I should be using the following code to empty the input variable in order to reset it:
cin.ignore(numeric_limits<streamsize>::max(), '\n');
But what am I missing here since it's not compiling, it says expected an identifier when mouse hover over max();
code:
int Menu::getInput()
{
int choice;
cin >> choice;
if (cin.fail())
{
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
return choice = 3;
}
return choice;
}
The suggestions that you've read to use ignore() are just band-aids designed to workaround broken logic that attempts to read newline delimited text using operator>>.
The general situation goes something like this: "well, I should be reading lines of text that contain two integers, followed by a text string, ok, so I'll use operator>> to read two integers, then a std::string, from std::cin". And that's fine, as long as we all live in a perfect world. Unfortunately, we don't live in a perfect world, and when bad input is encountered, in the middle of this odyssey, we end up with:
A std::istream in an error/fail state.
An undeterminate amount of consumed input, with a partially read line.
Recovering from this mess is what the using ignore() is supposed to achieve, by reading and discarding the remainder of the partially-consumed line, until the next newline characters.
But wouldn't you agree that it's much better not end up in this ugly mess to start with, in the first place?
And that's why if you need to read something that's formatted as lines of text, you should simply use std::getline(), to read one line of text at time. What a novel idea!
And then, once you've read the next line of text into a simple, std::string buffer, if you feel like it you can go ahead and construct a std::istringstream object from it, and use operator>> to your heart's content.
And if parsing the line of text, using std::istringstream, fails for some reason, you only need to issue an appropriate complaint to the user, then simply loop back, and use std::getline() to read the next line of text from your input time, without having to screw around with resetting the stream's status, or discarding partially-processed line. That seems to me like a much simpler, easier approach, doesn't it?
It appears to me that most C++ courses, instructors, and textbooks, are doing a disservice to their students by unceremoniously sprinkling liberal usage of operator>>, without properly explaining that this is a completely wrong approach to reading input that's formatted as newline-delimited lines of text. This just leads to nothing but utter confusion, and buggy code.
Take this as an answer to your question: scrap completely what you're doing, and simply rewrite it, simply as:
int Menu::getInput()
{
int choice;
std::string buffer;
if (!std::getline(std::cin, buffer).eof())
{
std::istringstream i(buffer);
i >> choice;
if (!i.fail())
return choice;
}
return 3;
}
Money back guarantee: this approach guarantees to never mess up your std::cin's state, or leave it with partially-consumed input.
I am trying to read from a file, and I have separated them by a new line character. I am using these code :
fstream input("wordfile.dat", ios::in);
char b[10];
while (!input.eof())
{
input.getline(b, 10);
cout << b << endl;
}
If I change the loop statement from while(!input.eof()) to while(input) , the program will output a blank line before the loop ends. But now it won't. The question is, in both statements the while condition must first input a line and by inputting it, it will know if it has reached end of file or if there is still more information. So input.eof() must act just like the other statement and output a blank line. First I thought it was a mistake, but I wondered why it was acting correctly. What is the difference between these two conditions?
Looking at operator bool we see ...
Notice that this function does not return the same as member good [...]
... that if (stream) is not the same as if (stream.good()), but also learn that it ...
Returns whether an error flag is set (either failbit or badbit).
So it's basically the same as not stream.fail() (which is true if either failbit or badbit is set).
This also explains the different behavior between while (stream) and while (not stream.eof()):
When the input file does not end with a newline, then stream.getline(buffer, size) will encounter the end of file before reaching the delimiting newline character (or the 10 character limit) and thus set the eofbit. Testing the stream with its operator bool will then be still true (since neither failbit nor badbit are set), and only after trying to read more using getline will set the failbit since no characters are extracted.
But when testing with not stream.eof(), the eofbit alone will end the loop.
If the stream is good, which is what you're testing with,
if (stream) // ...
then, this means that the stream is neither at the end of file (eof), nor bad nor failed.
So when it's not at the end of file, then it could still have failed or be in a bad state.
See the table here.
When reading (or writing) a stream, test for good unless you have a specific reason not to do so.
As a side note, this happens when you do input like the following, since getline returns a reference to the instance it's called on:
while (stream.getline(buffer, size)) {
// ..
}
First things first - I've got a text file in which there are binary numbers, one number for each row. I'm trying to read them and sum them up in a C++ program. I've written a function which transforms them to decimal and adds them after that and I know for sure that function's ok. And here's my problem - for these two different ways of reading a text file, I get different results (and only one of these results is right) [my function is decimal()]:
ifstream file;
file.open("sample.txt");
int sum = 0;
string BinaryNumber;
while (!file.eof()){
file >> BinaryNumber;
sum+=decimal(BinaryNumber);
}
and that way my sum is too large, but by a small quantity.
ifstream file;
file.open("sample.txt");
int sum = 0;
string BinaryNumber;
while (file >> BinaryNumber){
sum+=decimal(BinaryNumber);
}
and this way gives me the the right sum. After some testing I came to a conclusion that the while loop with eof() is making one more iteration than the other while loop. So my question is - what is the difference between those two ways of reading from a text file? Why the first while loop gives me the wrong result and what may be this extra iteration that it's doing?
The difference is that >> reads the data first, and then tells you whether it has been a success or not, while file.eof() does the check prior to the reading. That is why you get an extra read with the file.eof() approach, and that read is invalid.
You can modify the file.eof() code to make it work by moving the check to a place after the read, like this:
// This code has a problem, too!
while (true) { // We do not know if it's EOF until we try to read
file >> BinaryNumber; // Try reading first
if (file.eof()) { // Now it's OK to check for EOF
break; // We're at the end of file - exit the loop
}
sum+=decimal(BinaryNumber);
}
However, this code would break if there is no delimiter following the last data entry. So your second approach (i.e. checking the result of >>) is the correct one.
EDIT: This post was edited in response to this comment.
When using file.eof() to test the input, the last input probably fails and the value stays unchanged and is, thus, processed twice: when reading a string, the stream first skips leading whitespace and then reads characters until it finds a space. Assuming the last value is followed by a newline, the stream hasn't touched EOF, yet, i.e., file.eof() isn't true but reading a string fails because there are no non-whitespace characters.
When using file >> value the operation is executed and checked for success: always use this approach! The use of eof() is only to determine whether the failure to read was due to EOF being hit or something else.
Can anybody explain how this while condition works while accessing files in C++. In the while condition, employeeLine is a string.
while ( !inFile.getline( employeeLine, MAX_LINE, ‘\n’ ).eof( ) )
{
//some code here
}
If the file contains data like this then
will the code process the last line data or not as there is no newline character
Tomb33bb9.75<\n>
bbMarybb26bb10.15
(eof)
First of, inFile.getline(...) return the stream, i.e., a reference to inFile. When the stream reaches the end of file, the flag std::ios_base::eofbit gets set and inFile.eof() returns true. While there is any input, this flag won't be set. If the last line is incomplete, i.e., is lacking a newline character, it won't be processed!
Note, however, that the end of file may not necessarily be reached: if something goes wrong, std::ios_base::failbit is set and the stream won't response to any further attempts to read something: you'd have an infinite loop. std::istream::getline() does set std::ios_base::failbit when the line is too long to fit into the buffer (i.e., there are more than MAX_LINE - 1 characters). Another potential situation where the stream may go into failure mode without setting std::ios_base::eofbit is when an exception is thrown from the used stream buffer.
In general a better approach is to rely on the conversion to bool for a stream, i.e., to use
while (inFile.getline(employeeLine, MAX_LINE)) {
// ...
}
There is no need to pass '\n' as last parameter as it is the default. There is also no harm.
Note, that the above code won't deal with lines longer than MAX_LINE. That may be intentional, e.g., to avoid a denial of service attack based on infinitely large lines. Typically, it is preferable to use std::string, however:
for (std::string line; std::getline(inFile, line); ) {
// ...
}
If "inFile.getline( employeeLine, MAX_LINE, ‘\n’ ).eof( )" returns TRUE, it means that we reach the end of "inFile". So "!inFile.getline( employeeLine, MAX_LINE, ‘\n’ ).eof( )" means that we do not reach the end of "inFile".
See details in MSDN
I've got a program that is tailing a growing file.
I'm trying to avoid grabbing a partial line from the file (e.g. reading before the line is completely written by the other process.) I know it's happening in my code, so I'm trying to catch it specifically.
Is there a sane way to do this?
Here's what I'm trying:
if (getline (stream, logbuffer))
{
if (stream.eof())
{
cout << "Partial line found!" << endl;
return false;
}
return true;
}
return false;
However, I can't easily reproduce the problem so I'm not sure I'm detecting it with this code. std::getline strips off newlines, so I can't check the buffer for a trailing newline. My log message (above) is NEVER tripping.
Is there some other way of trying to check what I want to detect? Is there a way to know if the last line I read hit EOF without finding a EOL character?
Thanks.
This will never be true:
if (getline (stream, logbuffer))
{
if (stream.eof())
{
/// will never get here
If getline() worked, the stream cannot be in an eof state. The eof() and related state tests only work on the results of a previous read operation such as getline()- they do not predict what the next read will do.
As far as I know, there is no way of doing what you want. However, if the other process writes a line at a time, the problems you say you are experiencing should be very rare (non -existent in my experience), depending to some extent on the OS you are are using. I suspect the problem lies elsewhere, probably in your code. Tailing a file is a very common thing to do, and one does not normally need to resort to special code to do it.
However, should you find you do need to read partial lines, the basic algorithm is as follows:
forever do
wait for file change
read all possible input using read or readsome (not getline)
chop input into lines and possible partial line
process as required
end
An istream object such as std::cin has a get function that stops reading when it gets to a newline without extracting it from the stream. You could then peek() or get() it to see if indeed it is a newline. The catch is that you have to know the maximum length of a line coming from the other application. Example (untested) code follows below:
char buf[81]; // assumes an 80-char line length + null char
memset(buf, 0, 81);
if (cin.get(buf, 81))
{
if (cin.peek() == EOF) // You ran out of data before hitting end of line
{
cout << "Partial line found!\n";
}
}
I have to take issue with one statement you made here:
However, I can't easily reproduce the problem so I'm not sure I'm detecting it with this code.
It seems like from what you said it would be extremely easy to replicate your problem, if it is what you said. You can easily create a text file in some text editor - just make sure that the last like ends in an EOF instead of going on to a new line. Then point your program at that file and see what results.
Even if the other program isn't done writing the file, in the file that's where the line ends, so there's no way to tell the difference other than waiting to see if the other program writes something new.
edit: If you just want to tell if the line ends in a newline or not, you could write your own getline function that reads until it hits a newline but doesn't strip it.