ifstream question

B

Boltar

Hi

I'm using ifstream (which I hardly ever use) to read an ascii text
file (containing numbers delimited by newlines) in a loop using
something like:

ifstream infile("filename")
int value;

infile >> value;
while(!infile.eof() {
infile >> value;
:

}

The problem is if the file is missing a terminating newline the final
number is missed. How can I make sure its read anyway when the newline
is missing?

Thanks for any help

B2003
 
A

Alf P. Steinbach

* Boltar:
I'm using ifstream (which I hardly ever use) to read an ascii text
file (containing numbers delimited by newlines) in a loop using
something like:

ifstream infile("filename")
int value;

infile >> value;
while(!infile.eof() {
infile >> value;
:

}

The problem is if the file is missing a terminating newline the final
number is missed. How can I make sure its read anyway when the newline
is missing?

Don't test for eof, test for good stream state, i.e. that last read was
successful.

Like

ifstream infile( "filename" );

for( ;; )
{
int value;

infile >> value;
if( !infile.good() )
{
break;
}
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }

Disclaimer: I really hate iostreams, so possibly that last check isn't
completely 100% cover-all-cases or guaranteed to detect failure.


Cheers, & hth.,

- Alf
 
T

Thomas J. Gritzan

Alf said:
* Boltar:

Don't test for eof, test for good stream state, i.e. that last read was
successful.

The answer is in the FAQ:
http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.5
Like

ifstream infile( "filename" );

for( ;; )
{
int value;

infile >> value;
if( !infile.good() )
{
break;
}
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }

When I see this I start to believe, that break in loops is evil, as James
said. The example in the FAQ is not as ugly as this code.
 
A

Alf P. Steinbach

* Thomas J. Gritzan:
The answer is in the FAQ:
http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.5


When I see this I start to believe, that break in loops is evil, as
James said. The example in the FAQ is not as ugly as this code.

The FAQ code,

int i = 0;
while (std::cin >> x) { // RIGHT! (reliable)
++i;
// Work with x ...
}

is apparently simpler and shorter, because it uses implicit conversion,
it uses a side-effect based expression, it leaves out a declaration, it
exposes local variable to outside code, and it doesn't check for errors.

Judging from discussions here and elsewhere, few programmers understand
what that code /actually does/ -- and doesn't do.

To check own understanding, what does (std::cin >> x) as condition,
actually check?


Cheers, & hth.,

- Alf
 
T

Thomas J. Gritzan

Alf said:
The FAQ code,

int i = 0;
while (std::cin >> x) { // RIGHT! (reliable)
++i;
// Work with x ...
}

is apparently simpler and shorter, because it uses implicit conversion,
it uses a side-effect based expression, it leaves out a declaration, it
exposes local variable to outside code, and it doesn't check for errors.

You can check for errors after the loop as you did in your code. No problem
here.

Side-effect based expressions used with if or while are not unusual in C++.
std::getline is made so we are able to write
while (std::getline(/istream/, /string/)) { /*...*/ }
Also, some people use if with declaration and initialization to hide the
variable from outer scope:
if (Class* obj = dynamic_cast<Class*>(base)) { /*...*/ }
Its just a matter of taste/style, but this idioms are well known by C++
programmers.

The variable has to be declared before the loop, however exposing the local
variable is an issue.

So compare your code,

ifstream infile( "filename" );

for( ;; )
{
int value;

infile >> value;
if( !infile.good() )
{
break;
}
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }

with this:

ifstream infile( "filename" );
int value;

while (infile >> value)
{
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }
Judging from discussions here and elsewhere, few programmers understand
what that code /actually does/ -- and doesn't do.

To check own understanding, what does (std::cin >> x) as condition,
actually check?

std::cin or any std::ios instance used as condition is true if fail() is
not true, so (std::cin >> x) is same as (std::cin >> x, !std::cin.fail()).
fail() is true if failbit or badbit is set.
 
A

Alf P. Steinbach

* Thomas J. Gritzan:
You can check for errors after the loop as you did in your code. No
problem here.

Side-effect based expressions used with if or while are not unusual in
C++. std::getline is made so we are able to write
while (std::getline(/istream/, /string/)) { /*...*/ }
Also, some people use if with declaration and initialization to hide the
variable from outer scope:
if (Class* obj = dynamic_cast<Class*>(base)) { /*...*/ }
Its just a matter of taste/style, but this idioms are well known by C++
programmers.

The variable has to be declared before the loop, however exposing the
local variable is an issue.

So compare your code,

ifstream infile( "filename" );

for( ;; )
{
int value;

infile >> value;
if( !infile.good() )
{
break;
}
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }

with this:

ifstream infile( "filename" );
int value;

while (infile >> value)
{
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }

It would be

ifstream infile( "filename" );
{
int value;

while (infile >> value)
{
// Do something with value.
}
}
if (!infile.eof()) { /* oops. */ }

to be equivalent (except that my code should have used fail(): as I
wrote I couldn't guarantee correctness because I hate iostreams, i.e.
avoiding using them so that I would have to look up details, and one of
the things I really hate is that good() is not !fail(), and I can never
recall which is which, it's not exactly suggested by the names.).

If the purpose is to check how exit-in-middle fares wrt. placing first
half in the loop condition checking, then the exit-in-middle loop should
be coded in the same style, agree?, so compare above to

ifstream infile( "filename" );
for (;;)
{
int value;
if ((infile >> value).fail()) { break; }

// Do something with value.
}
if (!infile.eof()) { /* oops. */ }

Not much difference in clarity. I'd say the while loop is perhaps
marginally more clear because it's a more common idiom so that the eye
knows where to look, trained pattern recognition. The exit in the
middle loop however supports doing more work before the loop exit point,
without contorting that code to fit as a condition checking expression.

std::cin or any std::ios instance used as condition is true if fail() is
not true, so (std::cin >> x) is same as (std::cin >> x,
!std::cin.fail()). fail() is true if failbit or badbit is set.

Yeah, I'll take your word for it. :) But many programmers, including
me, don't go around with that in their heads. As mentioned, I can never
remember which of good() and !fail() is which, and that's one reason I
like to have the calls explicit in the code rather than implicit
conversion: it's one level less to check out in documentation when
things don't work as intended, which can happen with iostreams...


Cheers,

- Alf
 
J

Jim Langston

Alf said:
* Thomas J. Gritzan:

It would be

ifstream infile( "filename" );
{
int value;

while (infile >> value)
{
// Do something with value.
}
}
if (!infile.eof()) { /* oops. */ }

to be equivalent (except that my code should have used fail(): as I
wrote I couldn't guarantee correctness because I hate iostreams, i.e.
avoiding using them so that I would have to look up details, and one
of the things I really hate is that good() is not !fail(), and I can
never recall which is which, it's not exactly suggested by the
names.).
If the purpose is to check how exit-in-middle fares wrt. placing first
half in the loop condition checking, then the exit-in-middle loop
should be coded in the same style, agree?, so compare above to

ifstream infile( "filename" );
for (;;)
{
int value;
if ((infile >> value).fail()) { break; }

// Do something with value.
}
if (!infile.eof()) { /* oops. */ }

Not much difference in clarity. I'd say the while loop is perhaps
marginally more clear because it's a more common idiom so that the eye
knows where to look, trained pattern recognition. The exit in the
middle loop however supports doing more work before the loop exit
point, without contorting that code to fit as a condition checking
expression.

also, if scope is an issue why not:

{
ifstream infile( "filename" );
int value;
while ( infile >> value )
{
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }
}

Although the scope of an integer that I'm not going to use (for the same
purpose) after I'm done with it I'm not concerned about. If I really did
care I'd enclose it in brackets, no artifical for (;;) needed which I've
always considered to nothing much better that a goto.
 
T

Thomas J. Gritzan

Alf said:
If the purpose is to check how exit-in-middle fares wrt. placing first
half in the loop condition checking, then the exit-in-middle loop should
be coded in the same style, agree?, so compare above to

ifstream infile( "filename" );
for (;;)
{
int value;
if ((infile >> value).fail()) { break; }

I would do
if (!(infile >> value)) { break; }

See below.
// Do something with value.
}
if (!infile.eof()) { /* oops. */ }

Not much difference in clarity. I'd say the while loop is perhaps
marginally more clear because it's a more common idiom so that the eye
knows where to look, trained pattern recognition. The exit in the
middle loop however supports doing more work before the loop exit point,
without contorting that code to fit as a condition checking expression.

The common idiom is the key here. Less confusion, less documentation needed.
Yeah, I'll take your word for it. :) But many programmers, including
me, don't go around with that in their heads.

I looked it up. I don't go around with that too :)
As mentioned, I can never
remember which of good() and !fail() is which, and that's one reason I
like to have the calls explicit in the code rather than implicit
conversion: it's one level less to check out in documentation when
things don't work as intended, which can happen with iostreams...

I just trust the implicit "boolean" convertion of ios to be the right thing
here. If I see good() or fail() and I don't know exactly what they do, I
have to check it out in documentation too. fail() for example doesn't only
check for failbit as the name suggests, but for badbit and failbit.
With a common idiom or pattern, you know that you do the right thing.

The implicit convertion also fits with checking for null pointer:

if (ptr) instead of if (ptr != NULL)
if (!ptr) instead of if (ptr == NULL)

This is common in C and C++ as well and I know that some people don't like
it this way, too.

Just curious, what do you use instead of iostreams?
 
J

John Brawley

Alf P. Steinbach said:
* Boltar:

Don't test for eof, test for good stream state, i.e. that last read was
successful.

Like

ifstream infile( "filename" );

for( ;; )
{
int value;

infile >> value;
if( !infile.good() )
{
break;
}
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }

Disclaimer: I really hate iostreams, so possibly that last check isn't
completely 100% cover-all-cases or guaranteed to detect failure.

Pardon, please, but:
Oh. Good. _Grief_!!
Alf, I just spent manydays trying to do exactly this above (get a number
from a file), but with a piece of code another kind poster provided ('cause
I was stumped) and I modified.
To wit:

/*usual headers and using n.s.*/
ifstream ifstr("tvrfile.tvr");
string txt("");
char *tmp;
getline( ifstr, txt, ',');
const int pNum(strtod(txt.c_str(),&tmp));
cout << pNum << "\n"; //stick into pNum later

.........only to find in yours here --that I didn't even _need_ strod() !
(I thought his was elegant when I first saw it; I'm such a newbie....)

I compiled your code and ran it.
The numbers _are_ numbers, without strtod() !
I will study this thread intensely.
Is this sort of thing common?

_Thanks_! (many additional exclams)
What a great resource and learning aid you people are!
 
J

John Brawley

Alf P. Steinbach said:
* Boltar:

Don't test for eof, test for good stream state, i.e. that last read was
successful.

Like

ifstream infile( "filename" );

for( ;; )
{
int value;

infile >> value;
if( !infile.good() )
{
break;
}
// Do something with value.
}
if( !infile.eof() ) { /* oops. */ }

Disclaimer: I really hate iostreams, so possibly that last check isn't
completely 100% cover-all-cases or guaranteed to detect failure.

May I please ask further?
I ran this, modified as follows:
(usual up-top-stuff)

int main() {
ifstream infile("file.ext");
for(;;)
{
double value;
char toss; /*remove the commas (delimiter) & newlines*/
if(!infile.good()) {break;}
cout<<value<<" ";
}
infile>>value; cout<<value; /*kludge does get the final number....*/
if (!infile.eof()) {/*oops*/} /*my kludge for oops...(?)*/
return 0;
}

.......and it, too, misses the last number in the file without the post-loop
single extra use of infile>>value....
I tried many things, including the FAQ version, and putting an extra \n onto
the end of the file being read. No go; same problem.
All lines of file, example: 2.3,45.90,23,-0.2334,0.9\n
I get a beautiful sequence of every number in the file, exactly what I
wanted, and they are all _numbers_ (hallelujah), but the last one's not
there.

Your code modified to my need does everything I want it to except catch the
last number in the file _without_ the ugly kludge....

Is there more to this _neat_ clean way of getting my numbers from the file,
that catches the last one but also allows use of the char non-number
tosser?

Thanks!
 
A

Alf P. Steinbach

* John Brawley:
May I please ask further?
I ran this, modified as follows:
(usual up-top-stuff)

int main() {
ifstream infile("file.ext");
for(;;)
{
double value;
char toss; /*remove the commas (delimiter) & newlines*/
if(!infile.good()) {break;}
cout<<value<<" ";
}
infile>>value; cout<<value; /*kludge does get the final number....*/
if (!infile.eof()) {/*oops*/} /*my kludge for oops...(?)*/
return 0;
}

......and it, too, misses the last number in the file without the post-loop
single extra use of infile>>value....
I tried many things, including the FAQ version, and putting an extra \n onto
the end of the file being read. No go; same problem.
All lines of file, example: 2.3,45.90,23,-0.2334,0.9\n
I get a beautiful sequence of every number in the file, exactly what I
wanted, and they are all _numbers_ (hallelujah), but the last one's not
there.

Try 'infile.fail()' or just '!infile' instead of '!infile.good()'.

As I wrote, I hate 'em streams and so couldn't guarantee correctness.

I now believe after discussion else-thread that 'infile.fail()' or
equivalently just '!infile' will be correct.

This is something I have to learn anew every time discussion of iostream
details pops up.

I think it's like double-clutching for driving a Ford-T, you never
really forget, but never really remember it either (not that I've ever
had a Ford-T)...

Your code modified to my need does everything I want it to except catch the
last number in the file _without_ the ugly kludge....

Is there more to this _neat_ clean way of getting my numbers from the file,
that catches the last one but also allows use of the char non-number
tosser?

Not sure what you mean.


Cheers & hth.,

- Alf
 
J

John Brawley

(self-responding: idiot-reduction)

May I please ask further?
I ran this, modified as follows:
(usual up-top-stuff)

int main() {
ifstream infile("file.ext");
for(;;)
{
double value;
char toss; /*remove the commas (delimiter) & newlines*/
if(!infile.good()) {break;}

......and it, too, misses the last number in the file without the post-loop
single extra use of infile>>value....

<...>

Please forgive me for wasting your time.
I know what's wrong.
I fixed it.
I feel like an idiot.

Reverse the order of the two value-grabbers:
infile>>toss;
infile>>value;

This reverses the problem (putting the glitch at the front of the file),
which is not in this specific case a problem.
Here's my filereader almost ready for primetime:

int main() {
int pNum;
double pRad, gRad;
char toss;
ifstream infile("tvrfile.tvr");
infile>>pNum;
infile >>toss;
infile>>pRad;
infile>>toss;
infile>>gRad;
for( ;; )
{
double value;
infile >> toss;
infile >> value;
if(!infile.good() )
{
break;
}
cout << value<<" ";/*did it? yes!*/
/*put the values into my array[] here.*/
}
if( !infile.eof() ) { /* oops. */ }
infile.close();
return 0;
}

Because the first line of the input file is different, holding three
variables that don't go into the array, I can do: read,toss,read,toss,read
(but no toss paired to its read), _outside_ the for(;;) loop), which means
that inside the for(;;) loop where infile>>toss precedes infile>>value,
the first thing a read hits is the leftover newline, so the order of the
infile>> reads is perfect for the file I need to read.

I apologize for displaying such embarassing stupidity, putting the value
read before the toss read, so the last item in the file --a value-- gets
missed because the loop terminates before the read gets to it.

Suddenly I understand what a "half-loop" problem is.

Thank you; have a nice day....

(Alf, I write before reading you in response; I didn't want to feel too
stupid before I read you exposing _how_ stupid... (*smile*))
 
A

Alf P. Steinbach

* John Brawley:
(Alf, I write before reading you in response; I didn't want to feel too
stupid before I read you exposing _how_ stupid... (*smile*))

Well, you're not the one who's any reason to feel stupid: that would be
me, posting code I knew was likely to not be 100% correct, and saying so.

However, I don't feel stupid about not knowing or remembering everything
in the Universe.

Some folks (unfortunately this seems to include those who evaluate
people like me) seem to think that there are strict levels of knowledge
and understanding, where higher levels require complete mastering of
lower levels, such that if you're good, you know all of those lower
levels. Also, you then exude an extreme confidence and never question
your own opinion or statements. Well that just b*****it, IMHO, and in
particular one does not learn very much new by thinking one is never
wrong, but unfortunately that seems to be the way society works: a very
strong focus on appearance instead of substance. Of course it helps to
have both, and some of the /very/ good people are in that category.
Contrariwise, it doesn't in general help to give appearance of having no
self-confidence, although it can help in certain situations...


Cheers, & just be stubborn & patient -- you're on the right track ;-),

- Alf
 
K

kwikius

* John Brawley:









Try 'infile.fail()' or just '!infile' instead of '!infile.good()'.

As I wrote, I hate 'em streams and so couldn't guarantee correctness.

IMO the problem is that EOF is simultaneously a character in the
stream and some sort of abstraction representing the end of the
stream.

IIRC is.good() holds even if next op is is.get() == EOF;

is.good() is meant to be true if characters are available, but IMO if
the next char is EOF then by definition no characters are available.

Further if EOF is a character and you are at eof(), then you should
IMO be able to is.putback(), but IIRC you can't.

I opted to wrap up text streams, so they all have the same semantic,
for files, console and strings. Any stream has a name, for files the
name of the file, for strings a name you supply and for console the
name of the console ( and a terminator you supply) Also every stream
keeps a record of line and column position, so you can output error
messages including name, and current position in a user friendly
format. Final part is a checkpoint stack, so you can create
checkpoints with useful messages, and they pop off the stack at the
end of their scope. Another stream type is a stream stack, where you
can push and pop streams (say for #include files, or macros. Its not
perfect as ideally you should be able to pushback through eof, but
because of the semantics of std::stteams this isnt really feasible.

However I do despair of the std::streams ...

I expect I will hear other opinions from experts about the issue :)

regards
Andy Little
 
J

John Brawley

* John Brawley:
post-loop

<block quote>
IMO the problem is that EOF is simultaneously a character in the
stream and some sort of abstraction representing the end of the
stream.
<snips>
|
IIRC is.good() holds even if next op is is.get() == EOF;
is.good() is meant to be true if characters are available, but IMO if
the next char is EOF then by definition no characters are available.
|
Further if EOF is a character and you are at eof(), then you should
IMO be able to is.putback(), but IIRC you can't.
|
I opted to wrap up text streams, so they all have the same semantic,
for files, console and strings. Any stream has a name, for files the
name of the file, for strings a name you supply and for console the
name of the console ( and a terminator you supply) Also every stream
keeps a record of line and column position, so you can output error
messages including name, and current position in a user friendly
format. Final part is a checkpoint stack, so you can create
checkpoints with useful messages, and they pop off the stack at the
end of their scope. Another stream type is a stream stack, where you
can push and pop streams (say for #include files, or macros. Its not
perfect as ideally you should be able to pushback through eof, but
because of the semantics of std::stteams this isnt really feasible.
However I do despair of the std::streams ...
I expect I will hear other opinions from experts about the issue :)
/blockquote>

Thank you very much, Andy, this is useful info, but in my case the problem
was really my own stupidity: I was reading a file with number comma number
comma number newline number comma (etc), commas as delimiters, newlines for
human readbility.
Alf's code used one read in the loop, and I tried his code, which got the
first number and then stopped (next item was a comma, which didn't fit type
double, so the loop broke out).
I thought: why not add a second read, of char type (but use it like a
throwaway, never use the character collected by it), and see what happens?
What happened was the loop read the whole file (almost).
Bingo! Just what I wanted originally, had solved with dozens of lines of
ugly code, and now saw could be vastly easier and prettier. (It also
revealed another stupidity: I assumed --for no reason-- that the numbers in
the file were stored as characters. They were not. I didn't need to do any
strtod() or c_str() at all).
Anyway, what was wrong was that I had put the read-a-double instruction
first in the loop, and the read-a-character second, so when the loop
finished, the read-a-number instruction never got executed, leaving the last
number in the file still hanging out there.
Reversing the order of the reads _did_ get the last number, but would not
read the file _at_all_ since the _first_ thing in the file was a number and
now the loop was expecting a characer first.
BUT: O, beautiful synergy: as it happens, the first file line's three
numbers (the rest have five) are not intended to go into my x,y,z,r,c
database, so all I had to do was read these three and their two char type
commas (but _not_ read the newline) _outside_ of the loop, which then gave
the loop exactly what it expected (a char first --the \n-- in the loop-read)
when I went inside the loop to fill the database with numbers.
IOW, it was merely a matter of which, a number or a character, I read first
in the loop. If the number first, it missed the last number, and if the
char first, it missed the whole file. (I could also have forced my
file_writer_ to stick a comma into position 0 in the file, but that'd have
been a major kludge; ugly and obviously a very clumsy workaround).
Thanks for the more info, Andy (and Alf), but this seems to have been a case
of a neophyte (me) doing something _so_ obvious and stupid that the experts
here (you included) didn't see what sheer idiocy I had perpetrated upon
myself.
(*grin*)
 
J

John Brawley

Alf P. Steinbach said:
* John Brawley:

Well, you're not the one who's any reason to feel stupid: that would be
me, posting code I knew was likely to not be 100% correct, and saying so.

(*smile*) Yes, I do. A matter of mere sequence (structure), not language,
caused me a glitch, even though my resolution turned out to be elegant for
my particular need.

You, on the other hand....
Alf, your _"incorrect"_ code is lightyears beyond my _best_ code.
To me, "correct" code is code that compiles and runs. Period.
Yours compiled and ran unmodified, first time.
Y'all are programmers, so "correct" or "incorrect" may well be matters of
what's most efficient or sleek or functionally wise, and you will
argue/discuss over one method of doing something over another method (but
all of which work), while someone like me just wants to write code that
_works_ --no matter how procedurally or efficiently 'correct' it is.
Some folks (unfortunately this seems to include those who evaluate
people like me) seem to think that there are strict levels of knowledge
and understanding, where higher levels require complete mastering of
lower levels, such that if you're good, you know all of those lower
levels. Also, you then exude an extreme confidence and never question
your own opinion or statements. Well that just b*****it, IMHO, and in
particular one does not learn very much new by thinking one is never
wrong, but unfortunately that seems to be the way society works: a very
strong focus on appearance instead of substance. Of course it helps to
have both, and some of the /very/ good people are in that category.
Contrariwise, it doesn't in general help to give appearance of having no
self-confidence, although it can help in certain situations...
Cheers, & just be stubborn & patient -- you're on the right track ;-),

Thank You very much, Alf.
There may be also another side-effect of stratification of
knowledge-expected that you describe: there are also people like me out
here, either students trying to learn programming, or the occasional oddball
who just wants to write one program to do one thing, once in a lifetime, and
who can generate a problem for himself that is _so_ sophomorish and basic,
that experts who -are- in the upper reaches may not see what the problem
actually is. You do this extensively --I assume for a living, and you don't
really expect others to make so elementary a mistake as putting the cart
before the horse....
(*grin*)

Thanks again, Alf.
Filereader is ready, and is small, terse, clean, and easily integrated into
my existing program --thanks you and Andy and Balaji and many others.
Next: visuals by OpenGL. (*<shiver>*)
 
J

Jerry Coffin

[ ... ]
is.good() is meant to be true if characters are available, but IMO if
the next char is EOF then by definition no characters are available.

You're misinterpreting is.good(). It's meant to indicate the current
error state of the stream buffer. IOW, if the end of file has been
encountered, it should signal so -- but if it hasn't been encountered
yet, it should NOT signal so.

IMO, this is generally a good thing -- Pascal's I/O system worked as you
describe and it made writing correct loops considerably more difficult.
Further if EOF is a character and you are at eof(), then you should
IMO be able to is.putback(), but IIRC you can't.

Trivially true -- any statement of the form "if x then y' is true, but
meaningless if x if false. In this case, you start with the idea that
EOF is a character -- but since it's not, the rest of the statement
means nothing.
I opted to wrap up text streams, so they all have the same semantic,
for files, console and strings. Any stream has a name, for files the
name of the file, for strings a name you supply and for console the
name of the console ( and a terminator you supply) Also every stream
keeps a record of line and column position, so you can output error
messages including name, and current position in a user friendly
format. Final part is a checkpoint stack, so you can create
checkpoints with useful messages, and they pop off the stack at the
end of their scope. Another stream type is a stream stack, where you
can push and pop streams (say for #include files, or macros. Its not
perfect as ideally you should be able to pushback through eof, but
because of the semantics of std::stteams this isnt really feasible.

If you're wrapping the stream anyway, it's pretty trivial to add your
own capability to put things back, even when not supported by the stream
itself (just put whatever back into your own buffer, and have your input
routines check that buffer before reading from the stream).

Years ago, I wrote a wrapper for lexing C source code that wrapped C's
FILE * I/O. It allowed you to unget a more or less arbitrary amount of
data -- and unless memory serves me particularly badly today, it should
have still allowed you to unget (and read) data after the stream itself
had encountered EOF.
 
J

Jerry Coffin

[email protected] says... said:
BUT: O, beautiful synergy: as it happens, the first file line's three
numbers (the rest have five) are not intended to go into my x,y,z,r,c
database, so all I had to do was read these three and their two char type
commas (but _not_ read the newline) _outside_ of the loop, which then gave
the loop exactly what it expected (a char first --the \n-- in the loop-read)
when I went inside the loop to fill the database with numbers.
IOW, it was merely a matter of which, a number or a character, I read first
in the loop. If the number first, it missed the last number, and if the
char first, it missed the whole file. (I could also have forced my
file_writer_ to stick a comma into position 0 in the file, but that'd have
been a major kludge; ugly and obviously a very clumsy workaround).
Thanks for the more info, Andy (and Alf), but this seems to have been a case
of a neophyte (me) doing something _so_ obvious and stupid that the experts
here (you included) didn't see what sheer idiocy I had perpetrated upon
myself.
(*grin*)

I wouldn't be quite so hard on yourself -- things like this really can
be a pain for almost anybody to get right.

There are other ways that can be a bit easier though. For one example,
when you read numbers from a stream, the stream uses a locale to
actually read the numbers, as well as to classify the other characters
that get read from the stream. When you read numbers, it treats white-
space as delimiters between the numbers -- e.g. if you started with a
file like:

1 2 3 4 5

instead of:

1,2,3,4,5

it would have skipped the white-space automatically, and just read
numbers.

That leads to a fairly simple (if somewhat oddball) solution: create a
locale that says ',' is a whitespace character, and then tell the stream
to use that locale:

class my_ctype : public std::ctype<char> {
mask my_table[UCHAR_MAX];
public:
my_ctype(size_t refs = 0)
: std::ctype<char>(my_table, false, refs)
{
// copy the normal character class table, so ' ', tab, etc., are still
// white space
std::copy(classic_table(),
classic_table() + table_size,
my_table);

// for our purposes, comma is also white space.
my_table[','] = (mask)space;
}
};

int add(int a, int b) { return a+b; }

int main() {
// create a locale that includes our ctype:
std::locale x(std::locale::classic(), new my_ctype);

// and tell cin to use the new locale:
std::cin.imbue(x);

// now we'll read numbers from standard input into a vector
std::vector<int> numbers;

std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(numbers));

// print out the numbers:
std::copy(numbers.begin(), numbers.end(),
std::eek:stream_iterator<int>(std::cout, "\n"));
std::cout << "\n";

// add numbers to show they really are numbers:
std::cout << std::accumulate(numbers.begin(), numbers.end(),
0, add);

return 0;
}

input:
1,2,3,4,5

output:
1
2
3
4
5

15
 
J

John Brawley

(Top-posting hated,
bottom-posting proper;
where fits "middle-posting" ? (*grin*)
See below)
commas (but _not_ read the newline) _outside_ of the loop, which then > >
the loop exactly what it expected (a char first --the \n-- in the loop-
[...]
I wouldn't be quite so hard on yourself -- things like this really can
be a pain for almost anybody to get right.

There are other ways that can be a bit easier though. For one example,
when you read numbers from a stream, the stream uses a locale to
actually read the numbers, as well as to classify the other characters
that get read from the stream. When you read numbers, it treats white-
space as delimiters between the numbers -- e.g. if you started with a
file like:

Apart from the excellence of your info useful to many others no doubt, I
didn't know it'd be a 'standard' phenomenon that whitespaces would be
ignored in a program process with streams in C++. I know C++ ignores them
in a .cpp file to be compiled but it would not have occured to me that this
could be the case also within how a compiled .exe treats the stream.

That gives me a major reason to perhaps alter my file _writers_ to record
data in space-dlimited format instead of comma-delimited, which I'd been
resisting since the preceding Python version of this uses the
comma-delimited form.

As to your code (left in below), that's where others will benefit. It's
flat-out beyond my understanding. I haven't even figured out how to write a
class, just barely managed recently to get a few functions to work.
(Compiler laughs at me, says mine are an outmoded form --they look like
Python functions--but grudgingly compiles the program anyway...)

I appreciate the effort and idea, but that's a lot more code (even if I
understood it) than I want to have to do, just to deal with the commas (and
the newlines), when the "wrong" way (fooling the typed infile>> function
into doing what I want it to, without writing a purposeful fooler like this)
works perfectly with a tenth of the code.

I've said in another thread that I'm not a programmer. I'm _not_ a
programmer. I'm just a man who has no choice but to do one thing one time
in code so it will run on a computer (a model of something), and who has no
choice but to now use C++ to gain the sheer speed advantage over Python.
(The program is 95% continuous pure number-crunching; hopes for very large
data sets.)

Thanks for the info and sentiments. I do appreciate them.
1 2 3 4 5

instead of:

1,2,3,4,5

it would have skipped the white-space automatically, and just read
numbers.

That leads to a fairly simple (if somewhat oddball) solution: create a
locale that says ',' is a whitespace character, and then tell the stream
to use that locale:

class my_ctype : public std::ctype<char> {
mask my_table[UCHAR_MAX];
public:
my_ctype(size_t refs = 0)
: std::ctype<char>(my_table, false, refs)
{
// copy the normal character class table, so ' ', tab, etc., are still
// white space
std::copy(classic_table(),
classic_table() + table_size,
my_table);

// for our purposes, comma is also white space.
my_table[','] = (mask)space;
}
};

int add(int a, int b) { return a+b; }

int main() {
// create a locale that includes our ctype:
std::locale x(std::locale::classic(), new my_ctype);

// and tell cin to use the new locale:
std::cin.imbue(x);

// now we'll read numbers from standard input into a vector
std::vector<int> numbers;

std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(numbers));

// print out the numbers:
std::copy(numbers.begin(), numbers.end(),
std::eek:stream_iterator<int>(std::cout, "\n"));
std::cout << "\n";

// add numbers to show they really are numbers:
std::cout << std::accumulate(numbers.begin(), numbers.end(),
0, add);

return 0;
}

input:
1,2,3,4,5

output:
1
2
3
4
5

15

Thank you very much.
 
J

John Brawley

(very short)

"Jerry Coffin"
[ ... ]
BUT: O, beautiful synergy: as it happens, the first file line's three
numbers (the rest have five) are not intended to go into my x,y,z,r,c
[...]
I wouldn't be quite so hard on yourself -- things like this really can
be a pain for almost anybody to get right.

There are other ways that can be a bit easier though. For one example,
when you read numbers from a stream, the stream uses a locale to
actually read the numbers, as well as to classify the other characters
that get read from the stream. When you read numbers, it treats white-
space as delimiters between the numbers --

.....Not only does it treat whitespace as a delimiter (apparently any length
of whitespace), but also it treats newlines (\n) as if they also were
whitespaces.
That's fantastic.
I altered my existing file_writer_ to use whitespace instead of comma
delimiting, and my little filereader now flows numbers into the variable in
the loop with only 75% or so of the code I was using to do it with the
commas in place, and it remains user-readable and user-editable.
I can read the whole file with only infile>>value; in the loop.
_Thank_you_ for the hint.
 

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

Similar Threads

ifstream 5
Can ifstream read file more than 2G? 4
An ifstream question 0
ifstream errors 15
ifstream 5
ifstream and format issues 2
multi-thread and ifstream 1
copy std::cin to a ifstream 3

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top