?
Solved

Reading from text files - simple question

Posted on 2003-03-29
5
Medium Priority
?
184 Views
Last Modified: 2010-04-01
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
Comment
Question by:pete_bristol
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
  • 2
5 Comments
 
LVL 11

Expert Comment

by:bcladd
ID: 8230308
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
 

Author Comment

by:pete_bristol
ID: 8230508
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
 
LVL 11

Accepted Solution

by:
bcladd earned 200 total points
ID: 8230590
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
 
LVL 12

Expert Comment

by:Salte
ID: 8230928
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
 

Author Comment

by:pete_bristol
ID: 8232075
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

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
Suggested Courses
Course of the Month13 days, 17 hours left to enroll

801 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question