"inFile" object cannot read EOF

W

waltbrad

Hello. I'm studying the book "C++ Primer Plus" by Stephan Prata. In
chapter 6 he gives an exercise that reads from a file. The list is
thus:

4
Sam Stone
2000
Freida Flass
100500
Tammy Tubbs
5000
Rich Raptor
55000

So I create a istream object, inFile, and associate the file with
it. I know it reads the file because I've placed cout directives
after every read instruction. (since taken out). But I can't get the
program to read EOF.

After the following code there are conditions that test for
inFile.eof() and inFile.fail() but these get skipped over, to a
default "else" condition.

Can anyone see why EOF is not being recognized?


inFile >> size;
donor *pd = new donor[size];
while(inFile.get() != '\n')
;

for(int i = 0; i < size; i++){
getline(inFile, pd.name); //a cout stmt here and
inFile >> pd.cash; //here lets me know the data was
read
while(inFile.get() != '\n')
;
}
 
V

Victor Bazarov

waltbrad said:
Hello. I'm studying the book "C++ Primer Plus" by Stephan Prata. In
chapter 6 he gives an exercise that reads from a file. The list is
thus:

4
Sam Stone
2000
Freida Flass
100500
Tammy Tubbs
5000
Rich Raptor
55000

So I create a istream object, inFile, and associate the file with
it. I know it reads the file because I've placed cout directives
after every read instruction. (since taken out). But I can't get the
program to read EOF.

What do you mean by "to read EOF"? What's "EOF" in your statement?
After the following code there are conditions that test for
inFile.eof() and inFile.fail() but these get skipped over, to a
default "else" condition.

This is covered in the FAQ 5.8.
Can anyone see why EOF is not being recognized?

Every read operation ends with some kind of terminator. For example,
reading a number ('pd.cash') terminates at a symbol that cannot
be part of a number (like a newline). Most likely you never attempt
to read when the file cursor is _at_ the end of the file, to force the
'eof' bit to be set. Since you tell your program exactly how many
times to read, it doesn't fail to read, and that's why 'fail' bit is
not getting set either. Try removing the newline after the last
number in your file - make the third '0' in '55000' the _last_ symbol
in the file. You should see the difference.
inFile >> size;
donor *pd = new donor[size];
while(inFile.get() != '\n')
;

for(int i = 0; i < size; i++){
getline(inFile, pd.name); //a cout stmt here and
inFile >> pd.cash; //here lets me know the data was
read
while(inFile.get() != '\n')
;
}


V
 
W

waltbrad

This is covered in the FAQ 5.8.

Hello again. Thanks for the response. Actually I was getting around to
your answer. But, you confirmed my suspicions, and I didn't know a way
to test them.

I waasn't aware that there was a faq for this newsgroup. Where can I
find it?

Thanks again.
 
D

Devon Null

waltbrad said:
I waasn't aware that there was a faq for this newsgroup. Where can I
find it?

http://www.parashift.com/c++-faq-lite/

DN
--
[there are no x's in my email]

I have the right to remain silent
(and should probably use it as much as possible)
Anything I type can and will be used against me
in a court of idiocy
I have the right to be wrong
(and probably am)
If I can not furnish my own wrongness
I'm sure someone will provide it for me.
 
V

Victor Bazarov

waltbrad said:
Hello again. Thanks for the response. Actually I was getting around to
your answer. But, you confirmed my suspicions, and I didn't know a way
to test them.

I waasn't aware that there was a faq for this newsgroup. Where can I
find it?

http://www.parashift.com/c++-faq-lite/

Also, check out "Welcome to comp.lang.c++" message posted here every
now and then. If your server doesn't have it, search the archives.

V
 
D

Devon Null

waltbrad said:
I waasn't aware that there was a faq for this newsgroup. Where can I
find it?

http://www.parashift.com/c++-faq-lite/

Mostly a great reference for a lot of concepts in C++, but has a section
devoted entirely to posting in this NG.

DN
--
[there are no x's in my email]

I have the right to remain silent
(and should probably use it as much as possible)
Anything I type can and will be used against me
in a court of idiocy
I have the right to be wrong
(and probably am)
If I can not furnish my own wrongness
I'm sure someone will provide it for me.
 
J

James Kanze

Hello. I'm studying the book "C++ Primer Plus" by Stephan Prata. In
chapter 6 he gives an exercise that reads from a file. The list is
thus:
4
Sam Stone
2000
Freida Flass
100500
Tammy Tubbs
5000
Rich Raptor
55000
So I create a istream object, inFile, and associate the file with
it. I know it reads the file because I've placed cout directives
after every read instruction. (since taken out). But I can't get the
program to read EOF.

You can't "read" EOF, since by definition, a read fails if the
file is at EOF. You have to try to read something, and then
check whether it failed. After failure, you can check whether
it was due to EOF, or some other reason.
After the following code there are conditions that test for
inFile.eof() and inFile.fail() but these get skipped over, to a
default "else" condition.

I would help if you'd show them, however...
Can anyone see why EOF is not being recognized?
inFile >> size;
donor *pd = new donor[size];
while(inFile.get() != '\n')
;

Why not simply: inFile.ignore( INT_MAX, '\n' ) ?

More generally, your file is line oriented, so it is probably
better to read it line by line, using an istringstream to
convert the numeric values (and verifying that the format is
correct). So the above would read:

std::string line ;
if ( ! std::getline( inFile, line ) ) {
// Error, file was empty...
}
std::istringstream sLine( line ) ;
if ( ! (sLine >> size >> std::ws) || sLine.get() != EOF ) {
// Error, first line didn't contain a number, or
// had extra junk at the end...
}

Given that your format includes lines with just a single number,
I'd probably wrap this into a separate function.

Finally, there's no reason in C++ to have to specify the count
at the start. Just use std::vector, and push_back until you
encounter end of file.
for(int i = 0; i < size; i++){
getline(inFile, pd.name); //a cout stmt here and
inFile >> pd.cash; //here lets me know the data was
read
while(inFile.get() != '\n')
;
}


I'd write this loop something like:

std::vector< donor > donors ;
std::string line1 ;
std::string line2 ;
while ( getline( inFile, line1 ) && getline( inFile, line2 ) ) {
double cash ;
std::istringstream sCash( line2 ) ;
sCash >> cash ;
donors.push_back( donor( line1, cash ) ) ;
}

Except, of course, that it requires more error handling; the
above only works if the file has the correct format.
 
J

James Kanze

Also, check out "Welcome to comp.lang.c++" message posted here every
now and then. If your server doesn't have it, search the archives.

Note that all of the FAQ's, to all groups, are available at
www.faqs.org. Back in the old days, this was pointed out to
people when they got their connection, but any sort of
responsability on the part of providers today seems to have gone
out the window.
 
B

BobR

James Kanze wrote in message...
** You can't "read" EOF **, since by definition, a read fails if the
file is at EOF.

So, my docs are wrong?
" Method: int istream::get ()
Read a single character ** (or EOF) ** from the input stream, returning it
(coerced to an unsigned char) as the result. "
You have to try to read something, and then
check whether it failed. After failure, you can check whether
it was due to EOF, or some other reason.

I once did this in some 'test' code:

// std::vector<unsigned char> Image;
while( SomeInFile.peek() != EOF ){
// .... do stuff ....
// Image.push_back( SomeInFile.get() );
// if not reading single chars, need to check stream state.
}

Maybe it will help the OP to see the light. <G>
 
V

Victor Bazarov

BobR said:
James Kanze wrote in message...


So, my docs are wrong?
" Method: int istream::get ()
Read a single character ** (or EOF) ** from the input stream,
returning it (coerced to an unsigned char) as the result. "

Whoever wrote that decided to shorten it a bit. What it should say
is "Read a single character from the input stream returning it, or
return a special value if the stream is in 'end-of-file' state".

In fact, istream::get() returns either the character it read or
'traits::eof()', according to the Standard. It doesn't "read" any
"EOF" from the stream.
I once did this in some 'test' code:

// std::vector<unsigned char> Image;
while( SomeInFile.peek() != EOF ){
// .... do stuff ....
// Image.push_back( SomeInFile.get() );
// if not reading single chars, need to check stream state.
}

Maybe it will help the OP to see the light. <G>

Maybe...

V
 
B

BobR

Victor Bazarov wrote in message...
Whoever wrote that decided to shorten it a bit. What it should say
is "Read a single character from the input stream returning it, or
return a special value if the stream is in 'end-of-file' state".

Ok, I'll buy that. Thanks.
In fact, istream::get() returns either the character it read or
'traits::eof()', according to the Standard. It doesn't "read" any
"EOF" from the stream.

No "implementation defined" escape clause there? I'll check it out (for my
systems(win,GNU)) to satisfy my curiosity <G>.
 
J

James Kanze

James Kanze wrote in message...
So, my docs are wrong?
" Method: int istream::get ()
Read a single character ** (or EOF) ** from the input stream, returning it
(coerced to an unsigned char) as the result. "

Pourly worded. It "tries" to read a single character, returning
the character read, or EOF if it cannot read a character. EOF
is not a character, but a special out of band value. (That's
why get() returns int, and not char.)
I once did this in some 'test' code:
// std::vector<unsigned char> Image;
while( SomeInFile.peek() != EOF ){
// .... do stuff ....
// Image.push_back( SomeInFile.get() );
// if not reading single chars, need to check stream state.
}
Maybe it will help the OP to see the light. <G>

Yes. That's basically what I recommended at the filebuf level:
peek() simply returns rdbuf()->sgetc() (or EOF, if the rdbuf()
returns a null pointer).
 
J

James Kanze

Victor Bazarov wrote in message...
Ok, I'll buy that. Thanks.

The important point is that at this level, EOF is an out of band
value, and not a character. At this level, because...
No "implementation defined" escape clause there? I'll check it
out (for my systems(win,GNU)) to satisfy my curiosity <G>.

Most of what takes place in IO is more or less "implementation
defined". When it's not flattly unspecified In particular, how
an implementation represents line breaks and end of file is
unspecified, and may (and does under most systems) differ
between text files and binary. (For Unix, everything is pretty
much transparent, and there is no EOF character, either in text
or in binary files. I think that most Windows compilers,
however, still recognize the traditional CP/M end of file
character, 0x1A, in text files.)

Note that historically, a common error was writing the result of
istream::get() or istream::peek() (or earlier, getc() or
getchar()) to a char, and testing that for EOF (typically -1,
although any negative value is legal). This worked if (and only
if) plain char was signed, and the file never contained a 0xFF.
In ISO 8859-1, 0xFF is the 'ÿ' character (small Latin letter y
with diarese, if it doesn't show up right here). Not the most
common character, so the error often went unobserved.
 
J

Jerry Coffin

[ ... ]
I'd write this loop something like:

std::vector< donor > donors ;
std::string line1 ;
std::string line2 ;
while ( getline( inFile, line1 ) && getline( inFile, line2 ) ) {
double cash ;
std::istringstream sCash( line2 ) ;
sCash >> cash ;
donors.push_back( donor( line1, cash ) ) ;
}

Personally, I think I'd do it a bit differently:

struct donor {
std::string name;
double cash;

// read a single donor's data:
friend std::istream &operator>>(std::istream &i, donor &d) {
std::getline(i, d.name);

std::string line2;
std::getline(line2);
std::istringstream temp(line2);
line2 >> d.cash;
return i;
}
};

std::vector<donor> donors;

std::copy(std::istream_iterator<donor>(infile),
std::istream_iterator<donor>(),
std::back_inserter(donors));

This does not require (or allow) the count at the beginning of the file
though...
 
J

James Kanze

[ ... ]
I'd write this loop something like:
std::vector< donor > donors ;
std::string line1 ;
std::string line2 ;
while ( getline( inFile, line1 ) && getline( inFile, line2 ) ) {
double cash ;
std::istringstream sCash( line2 ) ;
sCash >> cash ;
donors.push_back( donor( line1, cash ) ) ;
}
Personally, I think I'd do it a bit differently:

I'd do it a lot differently in production code as well:).
Starting with a lot more error handling.
struct donor {
std::string name;
double cash;
// read a single donor's data:
friend std::istream &operator>>(std::istream &i, donor &d) {
std::getline(i, d.name);

std::string line2;
std::getline(line2);
std::istringstream temp(line2);
line2 >> d.cash;
return i;
}
};

Good point. Using a >> operator is definitely the way to go.
std::vector<donor> donors;

This does not require (or allow) the count at the beginning of the file
though...

Neither did my version:). In fact, as soon as you use
std::vector, you're freed from this constraint.
 
J

Jerry Coffin

On Jun 10, 2:23 am, Jerry Coffin <[email protected]> wrote:

[ ... ]
I'd do it a lot differently in production code as well:).
Starting with a lot more error handling.

Well yes, I'd hope so -- but I was talking about basic structure, and
(at least in theory) the error handling shouldn't affect that a whole
lot. Of course, the ability to do exactly that is one of the biggest
arguments in favor of exception handling...

[ ... ]
Neither did my version:). In fact, as soon as you use
std::vector, you're freed from this constraint.

Right -- of course, you can avoid it without using std::vector, if you
want to badly enough. Chances are, however, that the firt several times
you try, you won't do the job as well as std::vector does...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top