简体   繁体   中英

extraneous empty lines when reading from an ifstream

In my program, I've redirected stdout to print to a file 'console.txt'. A function writes to that file like this:

void printToConsole(const std::string& text, const TCODColor& fc, const TCODColor& bc)
    {
        // write the string
        cout << text << "@";

        // write the two color values
        cout << static_cast<int>(fc.r) << " "
             << static_cast<int>(fc.g) << " " 
             << static_cast<int>(fc.b) << " "
             << static_cast<int>(bc.r) << " "
             << static_cast<int>(bc.g) << " " 
             << static_cast<int>(bc.b) << " " << endl;
    }

I have a function that reads from that file that looks like this:

   void Console::readLogFile()
   {
        ifstream log("console.txt", ifstream::in);
        if(!log.is_open())
        {
            cerr << "ERROR: console.txt not found!" << endl;
            return;
        }

        // read new input into the stack
        char str[256];
        while(!log.eof())
        {
            log.getline(str, 256);
            cerr << "str: " << str << endl;
            stk.push(static_cast<string>(str));
            // stk is a std::stack<std::string> member of the class this function
            // belongs to.
        }
        cerr << endl;

        /* Do some stuff with str and stk here */

        log.close();
        clearLogFile();
    }

    void Console::clearLogFile()
    {
        FILE* log;
        log = fopen("console.txt", "w");
        fclose(log);
    }

Often, console.txt is empty when readLogFile is called. I would expect that the while(!log.eof()) loop would never execute in that case, but it does. There is always at least one extraneous blank line in the file, sometimes two, and when input is read from the file, the input line is sandwiched between two blank lines. After a few calls to this function, the while(!log.eof()) loop then goes into an infinite loop pulling blank lines from the file. A typical runthrough of the program looks like this:

str: 

str: Player moved.@191 191 191 0 0 0
str: 

str: 
str: Player moved.@191 191 191 0 0 0 
str: 

str: // there should be a 'Player moved.' line in here
str:

str: // here as well
str:

str: // also here
str:

str: 
str: Player moved.@191 191 191 0 0 0 
str: 

str:
str:
str:
str:
str:
str:
(onto infinite loop)

Can anyone see what I'm doing wrong here?

EDIT: As Amardeep suggested, I changed the while(!log.eof()) loop to a do{...}while(!log.fail); loop. This fixed the infinite loop problem, but not the extraneous lines. The program behaves as before, except where it once went into the infinite loop, it now reads nothing but blank lines where it should read input, like this:

str:

str:

str:

str: 
(etc.)

eof() status is not set until you attempt a read. You should change your read loop to do the getline() then check the fail() status instead of relying on eof(), which doesn't cover the breadth of things that can go wrong trying to read the file.

Standard anti patter for reading a file.

    while(!log.eof())
    {
        log.getline(str, 256);
        cerr << "str: " << str << endl;
        stk.push(static_cast<string>(str));
        // stk is a std::stack<std::string> member of the class this function
        // belongs to.
    }

try this:

    while(log.getline(str, 256))
    {
        cerr << "str: " << str << endl;
        stk.push(string(str));
    }

This works because the getline() method returns a reference to the stream.

When a stream is used in a boolean context it gets converted into a bool (for the pedantic not really but for the beginner as good as). If the stream is still in a good state after the read (ie the read worked) then it is converted to true. If the stream is in a bad state (ie the read failed) then it is converted to false. Therefore if the read worked the loop is entered. If the read failed (because maybe EOL is read) then the loop is not entered.

Note your version failed because you did not test the eof() after the read (getline()). The is because the last good read reads all the characters upto the EOF. But this means the eof flag is not set. It is not until you try to actually read past the EOF (this only happens if you read something after the last read read all the other characters) that the EOF flag is set.

PS. There is a free function that reads from a stream into a string.

std::string line;
std::getline(log, line);

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