• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 185
  • Last Modified:

Reading from text files - simple question

Hello All,

Please can somebody help me with the following extract of code.

If the text file contains "AB" it reports A:1 B:2
If the text file contains "BA" it reports A:2 B:1

i.e it always adds 1 onto the last character found!

No amount of following through the debugger etc helps, please can somebody fill in the gap in my understanding?

Many thanks

Pete


#include "Stdafx.h"
#include <iostream.h>
#include <fstream.h>


int main(int argc, char* argv[])
     {
     fstream File;
     File.open("c:\\pete.txt", ios::in);

     char Input;
        int CountA=0, CountB=0;

     while (!File.eof())
          {
          File >> Input;
          switch( (Input))
               {
               case 'A':
                    CountA+=1;
                    break;
               case 'B':
                    CountB+=1;
                    break;
               };
          }
     
     cout << "A:" << CountA << endl
          << "B:" << CountB << endl;
     
     return 0;
     }

0
pete_bristol
Asked:
pete_bristol
  • 2
  • 2
1 Solution
 
bcladdCommented:
When you read past the end of the file (which is what happens on the 3rd read with input "AB"), C++ does not change the value of the character read. Thus the B remains in the variable input and you count it again. The real problem here is that your loop runs one too many times and you happen to count the last character again during that extra loop.

It is typical to use a different while loop to read a file:

while (File >> input) {
  switch...
}

The read of the stream returns a reference to the stream (File) and streams have automatic conversion to bool. In this case, as the eof is set on the third read, the reference to File converts to false and the body of the loop is never executed so the "phantom" B is never counted.

Hope this helps, -bcl
0
 
pete_bristolAuthor Commented:
Thanks, it did & your suggestion certainly cured the problem.

However, i still dont fully understand why my code didn't work.

After reading in the first two chars, the counters are incremented etc as expected. Presumably on the next read, the file pointer is set too "eof" so the while loop terminates.

If this is the case (which i guess it isn't) why do the counters get incremented again when these are in the body of the loop?

Hope you can clarify

regards,

Pete



0
 
bcladdCommented:
This is hard without drawing and a lot of handwaving but here goes:

When you open the ifstream File, it maintains a read pointer. For our purposes the readpointer will be signified by "^". When the file is opened, the internal pointer is BEFORE the first character:

    File: ^AB !eof

!eof means that the eof flag is false

Also as your program begins and input is declared, input contains some random character. We will signify that character with a '#'.

    input: '#'

The state of your program can be captured in the value of File, input, countA, countB, and the line number to be executed next. I have copied the guts of your program and numbered the lines (minor editing changes for compactness):

 1    fstream File;
 2    File.open("c:\\pete.txt", ios::in);
 3    char Input;
 4    int CountA=0, CountB=0;
 5    while (!File.eof()) {
 6      File >> Input;
 7      switch( (Input)) {
 8        case 'A':
 9          CountA+=1;
10          break;
11        case 'B':
12          CountB+=1;
13          break;
14     };
15   }

So, first time we reach line 5, state of program is
    line: 5
    File: ^AB  !eof
    input: '#'
    CountA: 0
    CountB: 0

File's eof flag will be set ONLY after it reads PAST the end of file. That is it means PAST EOF, not JUST BEFORE EOF.

So, eof is false, we do the loop and we execute line 6. At the switch, state of program is:
    line: 7
    File: A^B  !eof
    input: 'A'
    CountA: 0
    CountB: 0

Next time we reach line 7, state is
    line: 7
    File: AB^  !eof
    input: 'B'
    CountA: 1
    CountB: 0

Note: as stated above, File.eof() is still false at this point. The file read pointer is just past the B, NOT past the EOF. No read has failed because of end of file.

NEXT TIME we reach line 7 (what, there's a next time?), we have tried to read past the end of file. C++ is smart, realizes there is a problem and DOES NOT CHANGE THE VARIABLE BEING READ. That is, line 6 sets File.eof() to true but does NOT change the value in input. Thus the state is:
    line: 7
    File: AB^  eof
    input: 'B'
    CountA: 1
    CountB: 1

So we go through the switch one more time before checking eof of File. Thus the state after the while loop is:

    line: AFTER WHILE
    File: AB^  eof
    input: 'B'
    CountA: 1
    CountB: 2

Hope that worked without a whiteboard.

-bcl
0
 
SalteCommented:
The error here is classic (i.e. it is very often occuring and most experts knows about it).

while (! file.eof()) {

will at start return false, then you read the 'A' and then it return false again and you read the B now you have actually reached the end of the file but as you read a B it isn't eof yet and so file.eof() will return false once again. This time you do another:

file >> Input;
now it is eof and the Input char isn't read so Input is stil the same value as it was last time ('A') and you therefore use that value without checking if you got eof from that read.

while (File >> Input) {

would be a better loop test.

Alf
0
 
pete_bristolAuthor Commented:
bcladd,

Thank you, your explanation was crystal clear - no whiteboard required!

And Alf, as ever, thank you for your reply.

I'm dead "chuffed" that i've fallen into a clasic error that others make as well - i must be on the right road then!

pete
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now