0

I have started working with files recently in class. When doing my homework, I can't really understand .eof().

The program which we are coding should do the following: the program asks you for a file name, opens it, and reads every character and counts every word. In the end it shows the average length of the words.

What happens is: Whenever I open a file with a text that ends with a dot, it works correctly, the average is correct. However, when the text does not end with a dot (for example: 9 8 7 6 5 4 3 2 1 Ignition), it shows that there are no words. I've been searching through the internet and I've only found that the End of File constant is normally -1. I'm just trying to figure out how it works. Thanks

The code is the following:

bool isSeparator (char lletra){ //Com que són caràcters anglesos no hem de tenir en compte els accents
    //Pre: cert
    //Post: retorna cert si lletra és un separador, fals altrament -- els números són entesos com separadors
    bool separador = true;
    if(lletra>='a' and lletra<='z')
        separador = false;
    else if(lletra>='A' and lletra<='Z')
        separador = false;
    else {
        separador = true;
    }
    return separador;
}

void calculateNumbers (string fileName){
    ifstream openFile (fileName.c_str());
    char lletra; //Iniciem la primera variable
    openFile>>lletra;
    double wordCounter, average, wordLength, totalLength;
    char auxiliar = ' ';
    wordCounter = average = wordLength = totalLength = 0;

    while (not openFile.eof()){ //Mentre no trobi el final, que continui mirant lletres
        if (not isSeparator(lletra)){
            wordLength++;
            auxiliar = lletra;
        } else if (isSeparator (lletra) and not isSeparator(auxiliar)){
            wordCounter++;
            totalLength+=wordLength;
            wordLength = 0;
            auxiliar = lletra;
        }
        openFile>>lletra;
    }
    cout<<setprecision(3);
    cout<<fixed;
    average = totalLength/wordCounter;
    if (average==0){
        cout<<"Mitjana longitud mot: cap mot!";
    } else {
        cout<<totalLength<<" "<<wordCounter<<endl;
        cout<<"Mitjana longitud mot: "<<average<<endl;
    }
    cout<<openFile.eof();
}

Some of the things are in catalan. If there's anything you don't understand just ask me.

drescherjm
  • 8,907
  • 5
  • 42
  • 60
  • 2
    ***how .eof () works in c++*** My advice is to avoid it because of this: https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong – drescherjm Nov 20 '18 at 19:17
  • 2
    EOF is a flag set when a read fails because the stream has reached the end of the file. Note this flag is set in response to a failed read, so testing for EOF before performing the read is a logic error. – user4581301 Nov 20 '18 at 19:25
  • Regard eof as a character: you cannot really know what's ahead before reading it. – zdf Nov 20 '18 at 19:28
  • The EOF constant is a helper when using a function that returns a single character. These functions typically return a character, so the EOF constant helps you determine that the character read doesn't exist in the file. Note that these functions return an `int` rather than a `char` so that you are unlikely to collide with a character set where -1 is used to represent a character. It is unlikely rather than impossible because some systems use the same size for `char` and `int` so that it IS possible to collide, but very rare. – user4581301 Nov 20 '18 at 19:33
  • 1
    Can you add the code for `isSeparator()`? – Johnny Mopp Nov 20 '18 at 19:37
  • @user4581301 `EOF` is a macro with a negative value, not a flag. Also that has nothing to do with this code, istream `>>` never writes `EOF` into the destination – M.M Nov 21 '18 at 20:26

1 Answers1

1

This question explains how .eof() works.

Your code actually reads the file correctly (although does not use recommended style). The lletra stores the latest character read from the file and the loop body is not entered once the characters have run out.


Your code only increments wordCounter when it encounters a separator. So when the file has ., wordCounter becomes 1. If the file has no . (and no other separator) it's always 0 even though the file had a "word" in it.

This makes the line average = totalLength/wordCounter; causes undefined behaviour by dividing by zero.

To fix this: you could add logic so that if the file does not end in a separator, then pretend that it did, so that you count the last word as a word. E.g. after the loop exits, check lletra and if it's not a separator then increment the word counter.

Also it would be better if the discrete variables were integer types instead of double.


NB. If you are actually trying to count the number of words in the file , by the normal meaning of "word", then the method you are using is not useful because you can not tell the difference between foo bar and foobar.

If you want to make lletra catch all the spaces then you need to change openFile>>lletra to openFile.get(lettra). The >> operator skips spaces. If you use the recommended style from the link in my first paragraph then you only need this change in one place, rather than changing both places in the code as it stands.

M.M
  • 130,300
  • 18
  • 171
  • 314