简体   繁体   中英

Read a file line by line in C++

I wrote the following C++ program to read a text file line by line and print out the content of the file line by line. I entered the name of the text file as the only command line argument into the command line.

#include <iostream>
#include <fstream>
using namespace std;

int main(int argc, char* argv[])
{
    char buf[255] = {};
    if (argc != 2)
    {
        cout << "Invalid number of files." << endl;
        return 1;
    }
    ifstream f(argv[1], ios::in | ios::binary);
    if (!f)
    {
        cout << "Error: Cannot open file." << endl;
        return 1;
    }

    while (!f.eof())
    {
        f.get(buf,255);
        cout << buf << endl;
    }
    f.close();
    return 0;
}

However, when I ran this code in Visual Studio, the Debug Console was completely blank. What's wrong with my code?

Apart from the errors mentioned in the comments, the program has a logical error because istream& istream::get(char* s, streamsize n) does not do what you (or I, until I debugged it) thought it does. Yes, it reads to the next newline; but it leaves the newline in the input!

The next time you call get(), it will see the newline immediately and return with an empty line in the buffer, for ever and ever.

The best way to fix this is to use the appropriate function, namely istream::getline() which extracts, but does not store the newline.

The EOF issue

is worth mentioning. The canonical way to read lines (if you want to write to a character buffer) is

  while (f.getline(buf, bufSz))
  {
    cout << buf << "\n";
  }

getline() returns a reference to the stream which in turn has a conversion function to bool, which makes it usable in a boolean expression like this. The conversion is true if input could be obtained. Interestingly, it may have encountered the end of file, and f.eof() would be true; but that alone does not make the stream convert to false . As long as it could extract at least one character it will convert to true , indicating that the last input operation made input available, and the loop will work as expected.

The next read after encountering EOF would then fail because no data could be extracted: After all, the read position is still at EOF. That is considered a read failure. The condition is wrong and the loop is exited, which was exactly the intent.

The buffer size issue

is worth mentioning, as well. The standard draft says in 30.7.4.3:

Characters are extracted and stored until one of the following occurs:

  1. end-of-file occurs on the input sequence (in which case the function calls setstate(eofbit));
  2. traits::eq(c, delim) for the next available input character c (in which case the input character is extracted but not stored);
  3. n is less than one or n - 1 characters are stored (in which case the function calls setstate( failbit)).

The conditions are tested in that order, which means that if n-1 characters have been stored and the next character is a newline (the default delimiter), the input was successful (and the newline is extracted as well).

This means that if your file contains a single line 123 you can read that successfully with f.getline(buf, 4) , but not a line 1234 (both may or may not be followed by a newline).

The line ending issue

Another complication here is that on Windows a file created with a typical editor will have a hidden carriage return before the newline, ie a line actually looks like "123\r\n" ("\r" and "\n" each being a single character with the values 13 and 10, respectively). Because you opened the file with the binary flag the program will see the carriage return; all lines will contain that "invisible" character, and the number of visible characters fitting in the buffer will be one shorter than one would assume.

The console issue;-)

Oh, and your Console was not entirely empty; it's just that modern computers are too fast and the first line which was probably printed (it was in my case) scrolled away faster than anybody could switch windows. When I looked closely there was a cursor in the bottom left corner where the program was busy printing line after line of nothing;-).

The conclusion

  • Debug your programs. It's very easy with VS.
  • Use getline(istream, string) .
  • Use the return value of input functions (typically the stream) as a boolean in a while loop: "As long as you can extract any input, use that input."
  • Beware of line ending issues.
  • Consider C I/O (printf, scanf) for anything non-trivial (I didn't discuss this in my answer but I think that's what many people do).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM