input output





[15] Input/output via <iostream.h> and <stdio.h>, C++ FAQ Lite







[15] Input/output via <iostream.h> and <stdio.h>
(Part of C++ FAQ Lite, Copyright © 1991-98, Marshall Cline, cline@parashift.com)

FAQs in section [15]:

[15.1] Why should I use <iostream.h> instead of the
traditional <stdio.h>?
[15.2] Why does my program go into an infinite loop when
someone enters an invalid input character?
[15.3] How does that funky while (cin >> foo) syntax
work?
[15.4] Why does my input seem to process past the end of file?
[15.5] Why is my program ignoring my input request
after the first iteration?
[15.6] How can I provide printing for my class Fred?
[15.7] How can I provide input for my class Fred?
[15.8] How can I provide printing for an entire hierarchy of
classes?
[15.9] How can I "reopen" cin and cout in binary
mode under DOS and/or OS/2?
[15.10] Why can't I open a file in a different directory
such as "..\test.dat"?



[15.1] Why should I use <iostream.h> instead of the
traditional <stdio.h>?
Increase type safety, reduce errors, improve performance, allow extensibility,
and provide subclassability.
printf() is arguably not broken, and scanf() is perhaps livable despite
being error prone, however both are limited with respect to what C++ I/O can
do. C++ I/O (using << and >>) is, relative to C (using printf() and
scanf()):

Better type safety: With <iostream.h>, the type of object
being I/O'd is known statically by the compiler. In contrast, <stdio.h> uses
"%" fields to figure out the types dynamically.
Less error prone: With <iostream.h>, there are no
redundant "%" tokens that have to be consistent with the actual objects
being I/O'd. Removing redundancy removes a class of errors.
Extensible: The C++ <iostream.h> mechanism allows new
user-defined types to be I/O'd without breaking existing code. Imagine the
chaos if everyone was simultaneously adding new incompatible "%" fields
to printf() and scanf()?!).
Subclassable: The C++ <iostream.h> mechanism is built from
real classes such as ostream and istream. Unlike <stdio.h>'s
FILE*, these are real classes and hence subclassable. This means you
can have other user-defined things that look and act like streams, yet that do
whatever strange and wonderful things you want. You automatically get to use
the zillions of lines of I/O code written by users you don't even know, and
they don't need to know about your "extended stream" class.

[ Top | Bottom | Previous section | Next section ]


[15.2] Why does my program go into an infinite loop when
someone enters an invalid input character?
For example, suppose you have the following code that reads integers from
cin:

    #include <iostream.h>
    
    main()
    {
      cout << "Enter numbers separated by whitespace (use -1 to quit): ";
      int i = 0;
      while (i != -1) {
        cin >> i;        // BAD FORM — See comments below
        cout << "You entered " << i << '\n';
      }
    }

The problem with this code is that it lacks any checking to see if someone
entered an invalid input character. In particular, if someone enters
something that doesn't look like an integer (such as an 'x'), the stream cin
goes into a "failed state," and all subsequent input attempts return
immediately without doing anything. In other words, the program enters an
infinite loop; if 42 was the last number that was successfully read,
the program will print the message You entered 42 over and over.
An easy way to check for invalid input is to move the input request from the
body of the while loop into the control-expression of the while loop.
E.g.,

    #include <iostream.h>
    
    main()
    {
      cout << "Enter a number, or -1 to quit: ";
      int i = 0;
      while (cin >> i) {    // GOOD FORM
        if (i == -1) break;
        cout << "You entered " << i << '\n';
      }
    }

This will cause the while loop to exit either when you hit end-of-file, or when
you enter a bad integer, or when you enter -1.
(Naturally you can eliminate the break by changing the while loop
expression from while (cin >> i) to
while ((cin >> i) && (i != -1)), but that's not
really the point of this FAQ since this FAQ has to do with iostreams rather
than generic structured programming guidelines.)
[ Top | Bottom | Previous section | Next section ]


[15.3] How does that funky while (cin >> foo) syntax
work?
See the previous FAQ for an example of the
"funky while (cin >> foo) syntax."
The expression (cin >> foo) calls the appropriate operator>>
(for example, it calls the operator>> that takes an istream on
the left and, if foo is of type int, an int& on the right). The
istream operator>> functions return their left argument by
convention, which in this case means it will return cin. Next the compiler
notices that the returned istream is in a boolean context, so it calls
the "cast" operator istream::operator bool(). I.e., in this case, it
calls cin.operator bool(), just as if you had casted it explicitly such
as (bool)cin or bool(cin). (Note: if your compiler doesn't yet
support the bool type, istream::operator void*() will be called
instead.)
The operator bool() cast operator returns true if the stream is in a
good state, or false if it's in a failed state (in the void* case, the
return values will be some non-NULL pointer or the NULL pointer,
respectively). For example, if you read one too many times (e.g., if you're
already at end-of-file), or if the actual info on the input stream isn't valid
for the type of foo (e.g., if foo is an int and the data is
an 'x' character), the stream will go into a failed state and the cast operator
will return false.
The reason operator>> doesn't simply return a bool indicating whether it
succeeded or failed is to support the "cascading" syntax:

      cin >> foo >> bar;

The operator>> is left-associative, which means the above is parsed as:

      (cin >> foo) >> bar;

In other words, if we replace operator>> with a normal function name such as
readFrom(), this becomes the expression:

      readFrom( readFrom(cin, foo), bar);

As always, we begin evaluating at the innermost expression. Because of the
left-associativity of operator>>, this happens to be the left-most
expression, cin >> foo. This expression returns cin (more precisely,
it returns a reference to its left-hand argument) to the next expression. The
next expression also returns (a reference to) cin, but this second reference
is ignored since it's the outermost expression in this "expression statement."
[ Top | Bottom | Previous section | Next section ]


[15.4] Why does my input seem to process past the end of file?
Because the eof state is not set until after a read is attempted past the
end of file. That is, reading the last byte from a file does not set the
eof state.
For example, the following code has an off-by-one error with the count i:

    int i = 0;
    while (! cin.eof()) {   // WRONG!
      cin >> x;
      ++i;
      // Work with x ...
    }

What you really need is:

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

[ Top | Bottom | Previous section | Next section ]


[15.5] Why is my program ignoring my input request
after the first iteration?
Because the numerical extractor leaves non-digits behind in the input buffer.
If your code looks like this:

    char name[1000];
    int age;
    
    for (;;) {
      cout << "Name: ";
      cin >> name;
      cout << "Age: ";
      cin >> age;
    }

What you really want is:

    for (;;) {
      cout << "Name: ";
      cin >> name;
      cout << "Age: ";
      cin >> age;
      cin.ignore(INT_MAX, '\n');
    }

[ Top | Bottom | Previous section | Next section ]


[15.6] How can I provide printing for my class Fred?
Use operator overloading to provide a
friend left-shift operator, operator<<.

    #include <iostream.h>
    
    class Fred {
    public:
      friend ostream& operator<< (ostream& o, const Fred& fred);
      // ...
    private:
      int i_;    // Just for illustration
    };
    
    ostream& operator<< (ostream& o, const Fred& fred)
    {
      return o << fred.i_;
    }
    
    main()
    {
      Fred f;
      cout << "My Fred object: " << f << "\n";
    }

We use a non-member function (a friend in this case)
since the Fred object is the right-hand operand of the << operator.
If the Fred object was supposed to be on the left hand side of the <<
(that is, myFred << cout rather than cout << myFred), we could
have used a member function named operator<<.
Note that operator<< returns the stream. This is so the output operations
can be cascaded.
[ Top | Bottom | Previous section | Next section ]


[15.7] How can I provide input for my class Fred?
Use operator overloading to provide a
friend right-shift operator, operator>>. This is
similar to the output operator, except the
parameter doesn't have a const: "Fred&"
rather than "const Fred&".

    #include <iostream.h>
    
    class Fred {
    public:
      friend istream& operator>> (istream& i, Fred& fred);
      // ...
    private:
      int i_;    // Just for illustration
    };
    
    istream& operator>> (istream& i, Fred& fred)
    {
      return i >> fred.i_;
    }
    
    main()
    {
      Fred f;
      cout << "Enter a Fred object: ";
      cin >> f;
      // ...
    }

Note that operator>> returns the stream. This is so the input operations can
be cascaded and/or used in a while loop or if
statement.
[ Top | Bottom | Previous section | Next section ]


[15.8] How can I provide printing for an entire hierarchy of
classes?
Provide a friend operator<<
that calls a protected virtual function:

    class Base {
    public:
      friend ostream& operator<< (ostream& o, const Base& b);
      // ...
    protected:
      virtual void print(ostream& o) const;
    };
    
    inline ostream& operator<< (ostream& o, const Base& b)
    {
      b.print(o);
      return o;
    }
    
    class Derived : public Base {
    protected:
      virtual void print(ostream& o) const;
    };

The end result is that operator<< acts as if it was dynamically
bound, even though it's a friend function. This is
called the Virtual Friend Function Idiom.
Note that derived classes override print(ostream&) const. In
particular, they do not provide their own operator<<.
Naturally if Base is an ABC,
Base::print(ostream&) const can be declared pure virtual using the "= 0" syntax.
[ Top | Bottom | Previous section | Next section ]


[15.9] How can I "reopen" cin and cout in binary
mode under DOS and/or OS/2?
This is implementation dependent. Check with your compiler's documentation.
For example, suppose you want to do binary I/O using cin and cout. Suppose
further that your operating system (such as DOS or OS/2) insists on translating
"\r\n" into "\n" on input from cin, and "\n" to
"\r\n" on output to cout or cerr.
Unfortunately there is no standard way to cause cin, cout, and/or cerr to
be opened in binary mode. Closing the streams and attempting to reopen them in
binary mode might have unexpected or undesirable results.
On systems where it makes a difference, the implementation might provide a way
to make them binary streams, but you would have to check the manuals to find
out.
[ Top | Bottom | Previous section | Next section ]


[15.10] Why can't I open a file in a different directory
such as "..\test.dat"?
Because "\t" is a tab character.
You should use forward slashes in your filenames, even on an operating system
that uses backslashes such as DOS, Windows, OS/2, etc. For example:

    #include <iostream.h>
    #include <fstream.h>
    
    main()
    {
      #if 1
        ifstsream file("../test.dat");     // RIGHT!
      #else
        ifstsream file("..\test.dat");     // WRONG!
      #endif
    
      // ...
    }

Remember, the backslash ("\") is used in string literals to create
special characters: "\n" is a newline, "\b" is a backspace, and
"\t" is a tab, "\a" is an "alert", "\v" is a
vertical-tab, etc. Therefore the file name
"\version\next\alpha\beta\test.dat" is interpreted as a bunch of very
funny characters; use "/version/next/alpha/beta/test.dat" instead, even
on systems that use a "\" as the directory separator such as DOS,
Windows, OS/2, etc.
[ Top | Bottom | Previous section | Next section ]


 E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised May 27, 1998




Wyszukiwarka