C++ Intro

Stream Input/Output


Stream Input/Output


No significant program is written in just a bare programming language. C++ provides its standard library as part of every complete C++ implementation. C++ input/output facilities are included in the library. This facility is commonly known as stream input/output, which supplies a set of functions and objects intended to solve the following problems:

  1. Standard Output: An ability to convert program variables of any type into a sequence of characters, and than write them to cout , a console output device (your command window display). Using command window, the standard output can be redirected into a file or "piped" as an input to another program.

  2. Standard Input : A set of facilities to request a sequence of characters from keyboard and convert it into a variable of desired type. Again, command window could redirect I/O to receive input from a file or another program.

  3. Formatting : There are often specific requirements for the layout of the output. For example, an integer of type int may have to be printed as a decimal number, or as a hexadecimal memory address. A floating-point number must often appear with exactly specified precision, and so on.

  4. File I/O: By default, every C++ program uses standard streams: standard output is cout, standard input - cin, and error output - cerr. To use other devices or files, task-specific streams must be created and attached to those files or devices. Such streams include mechanisms for opening and closing files, as well as attaching streams to particular files on disk.

Adding I/O to your programs can be tedious and somewhat challenging task. In our course we attempt "by example" approach to discover possible I/O solutions. When possible, we may encapsulate individual tasks into a stand-alone functions with relatively simple and well-documented set of parameters. All students are encouraged to use this improvised I/O library in their homeworks. Such approach is dictated by the fact that complete understanding of the stream manipulation syntax requires understanding of C++ user-defined types, operator overloading, and templates, all to be covered in the future.

The concept of streams is used for transmitting objects between machines and networks, for encrypting messages, for data compression, and for persistent storage of objects, to name a few. However, our discussion of streams is restricted to a simple character-oriented input and output.


Standard Output


The predefined object std::cout is an std::ostream instance that is attached to the standard output device, such as the console screen.

std::cout << Formatted Output

Standard output stream is simply a mechanism for converting values of various types into sequences of characters:

    #include <iostream>
    int main()
    {
        std::cout << "Hello, world!";
        return 0;
    }

As you can imagine, characters are transmitted to a particular device using low-level output implementation. The <iostream> library header defines output for every built-in C++ type. For example,

    #include <iostream>
    int main()
    {
        int i = 1;
        double d = 0.5;
        std::cout << i << '\t' << d;
        return 0;
    }

Output can be chained, because the result of each std::cout << expr is a reference to std::cout itself, therefore next << operator on the right side works in exact same manner as the previous one:

    std::cout << i << '\t' << d;

is equivalent to

    ( ( std::cout << i ) << '\t' ) << d;

cout.put( ), Unformatted Output of Individual Characters

Output of individual characters is intended for output operations of any number of individual characters using member function cout.put( ):

    std::cout.put( 'A' );
                    

Unformatted I/O using cin.read( ), cout.write( ) and cin.gcount( ). A call to cout.write( ) outputs a number of bytes from an array of characters in memory. These bytes are not formatted in any way. They are input or output as raw bytes.

As a general rule, these low-level I/O functions recommended for programs where implementation efficiency is at a premium, and should be primarily reserved for the implementation of higher-level functions.


Standard Input


The predefined object std::cin is an std::istream instance that is attached to the standard input device, such as the keyboard.

std::cin >> Formatted Input

The >> operator is intended for formatted input; that is, reading objects of an expected type and format. For example, because operator >> skips whitespace, you can read a sequence of whitespace-separated integers like this:

    #include <iostream>
    int main()
    {
        int one;
        int two;
        std::cout << "Please enter two integers: "; // Display message
        std::cin >> one >> two; // Get two integers from user
        return 0;
    }

Whitespace is defined as the standard blank, tab, newline, formfeed, and carriage return. A non-integer on the input will cause the input operation to fail and thus terminate the input process.

cin.get( ), Unformatted Input of Individual Characters

Where this formatted input is not desirable and we want to read a stream of individual characters and be able to examine each, we can employ the cin.get( ) function.

The cin.get( ) and cin.getline( ) functions treat whitespace characters exactly like other characters. They are intended for input operations where one does not make assumptions about the meanings of the characters read.

Unformatted input of individual characters allows input of multiple lines of text at once. Here is an example of cin.eof( ), cin.get( ), and cout.put( ), used together:

    #include <iostream>
    int main()
    {
        int onechar; // Note: char cannot represent EOF
        while ( ( onechar = std::cin.get() ) != EOF ) {
            std::cout.put( onechar );
        }
        return 0;
    }

As you can see, if there is no input character to return, cin.get( ) returns a suitable "end-of-file" marker, EOF.

cin.read( ), Raw Input

Unformatted I/O using cin.read( ), cout.write( ) and cin.gcount( ) provides a set of facilities to perform I/O with sequences of bytes of the specified length. A call to cin.read( ) inputs a requested number of bytes from standard input and stores the data in array of characters. These bytes are not formatted in any way. They are input or output as raw bytes.

A call cin.read( p, n ) reads at most n characters into p [0 ]...p [n - 1]. The read function does not rely on a terminator, and it doesn't put a terminating 0 into its target. Consequently, it really can read n characters (rather than just n - 1 ). In other words, it simply reads characters and doesn't try to make its target into a C-style string.

The cin.ignore( ) function reads characters like cin.read( ), but it doesn't store them anywhere. Like cin.read( ), it really can read n characters (rather than n - 1 ). The default number of characters read by cin.ignore( ) is 1 , so a call of cin.ignore( ) without an argument means to throw the next character away . Like getline( ), it optionally takes a terminator and removes that terminator from the input stream if it gets to it. Note that ignore( )'s default terminator is end-of-file.

cin.getline( ), Line-by-Line Input of Text

A couple of examples using std::cin.getline( ):

    #include <iostream>
    int main()
    {
        const int INPUT_SIZE = 256;
        char array_buffer[ INPUT_SIZE ];
        
        std::cout << "Please enter a line of text and hit enter: ";
        std::cin.getline( array_buffer, INPUT_SIZE );
        std::cout << "You entered: " << array_buffer << std::endl;

        std::cout << "Please enter text, terminated by semicolon: ";
        std::cin.getline( array_buffer, INPUT_SIZE, ';' );
        std::cout << "You entered: " << array_buffer << std::endl;
        return 0;
    }

String Input/Output


The standard library provides a string type to complement the string literals like "Hello, World!" The string type offers a variety of useful string operations, such as concatenation of fragments of text and many other features. At this point we will focus on the topic of string streams. A string stream acts as a buffer to accumulate and consume sequences of characters in stream-like manner. All stream I/O operations discussed earlier can be applied to string streams.

Interaction between std::cin, std::stringstream, and std::string is demonstrated by the sample program strstream.cpp (download source code here )

When efficiency isn't paramount, is it always better to read into a string. In fact, this should be a standard approach to get an arbitrary line of text from the user directly into std::string:

    #include <iostream>
    int main()
    {
        std::cout << "Please enter a line of text and hit enter: ";
        std::string input;
        std::getline( std::cin, input );
        std::cout << "You entered: " << input << std::endl;
        return 0;
    }

This little program extracts a string from the standard input. Again, this is the most common way of getting unformatted line of characters from the user.

Another, slightly more complicated example, adds code to guarantee that program is capable of reading input of a particular length:

    #include <iostream>
    const int CUSTOM_BUFFER_SIZE =    256; // read up to 256 characters
    int main()
    {
        std::cout << "Please enter a line of text and hit enter: ";
        std::string input;
        if ( input.capacity() < CUSTOM_BUFFER_SIZE ) {
            // reserve increases the size of data accepted by getline(): 
            input.reserve( CUSTOM_BUFFER_SIZE );
        }
        std::getline( std::cin, input );
        std::cout << "You entered: " << input << std::endl;
        return 0;
    }

Input/Output Formatting


Sample program, strstr_demo.cpp (download), provides introduction to I/O manipulators.

Another program, manipulators.cpp (download), demonstrates I/O formatting using stream manipulators. As promised earlier, actual low-level string stream implementation is hidden by a set of functions in string_format.h (download) header file. Here is a smaller version of the manipulators.cpp:

    #include <iostream>
    #include <iomanip> // needed for std::setw()
    #include "string_format.h"
    int main()
    {
        std::string input = "1234.56789";
        double value;
        if ( string2type( input, value, std::fixed ) ) {
            std::cout << std::setw( 12 ) << input << '\t';
            std::cout << std::setw( 12 ) << std::fixed << value << '\t';
            std::cout << std::setw( 12 ) << type2string( value, std::fixed );
            std::cout << "\t std::fixed";
        } else {
            std::cout << "BAD input: " << input;
        }
        return 0;
    }

Closer examination of this demo program yields the following observations:

  • First, a local string named input is instantiated with initial text that looks like a floating-point number, "1234.56789".

  • To the implementation, the content of the input is foreign, presented by some external format, better understood by the user, rather than a machine. Therefore, a special conversion is needed to translate the input into manageable data type, which is a double in our case. Conversion can either accept or reject the input due to the incorrect format. If accepted, the floating-point number will be stored in the variable named value.

  • Function string2type( ) attempts to convert from text "1234.56789" to a double. Additionally, we ask that the value should be handled as a fixed-point number by specifying std::fixed manipulator.

  • If string2type( ) conversion succeeds, it returns true, and then we display on the screen the original input, the converted value, and another string, created on the fly by type2string( ) .

  • Function type2string( ) does the opposite conversion; it takes the value of type double as its first parameter and returns its result in the form of a string. Textual representation format is specified by the second parameter, std::fixed .

  • Manipulator std::fixed gives consistent format to the input conversion, to the displayed value, and to the opposite conversion from double to string . However, such symmetrical layout is not a requirement; we could use other formats in each place if we wanted to.

  • If string2type( ) fails, it returns false , so program gets a chance to display "BAD input" message to the user.

  • Using std::setw( ), or any other parameterized manipulator requires the inclusion of the <iomanip> header file. The std::setw( 12 ) manipulator allows to right-justify the output by 12 characters. Justification gives our output a pretty look by organizing data into distinctly aligned columns.

Full version of manipulators.cpp generates this output:

  1234.56789     1234.567890     1234.567890    std::fixed
  1234.56789     1234.567890        1234.568    std::fixed + precision == 3
        56e2     5600.000000            5600
       56e+2     5600.000000         5600.00    std::showpoint
    234.5678    2.345678e+002   2.345678e+002   std::scientific
         255             255             255
          FF              ff              ff    std::hex
         777             777             777    std::oct
        true            true            true    std::boolalpha
           1            true               1

Some additional manipulator flags that could be used with other data types are:

  • boolalpha: use symbolic representation of true and false when inserting or extracting bool values. By default, bool values inserted and extract as numeric values 1 and 0.

  • dec: insert or extract integer values in decimal (base 10) format.

  • fixed: insert floating-point values in fixed-point format (with no exponent field). For example, default format for 1234.56789 is 1234.57, fixed makes it 1234.56789.

  • hex: insert or extract integer values in hexadecimal (base 16) format, such as "0xFF" or simply "FF ".

  • oct: insert or extract integer values in octal format, e.g. "077 "

  • scientific: insert floating-point values in scientific format (with an exponent field). For example, default format for 1234.56789 is 1234.567, scientific makes it 1.234568e+03.

  • showbase: insert a prefix that reveals the base of a generated integer field.

  • showpoint: insert a decimal point unconditionally in a generated floating-point field.

  • showpos: insert a plus sign in a nonnegative generated numeric field.

  • skipws: skip leading white space before certain extractions.

  • uppercase: insert uppercase equivalents of lowercase letters in certain insertions.

Conclusion

As a general rule, standard library string streams should be preferred for all in-memory formatting tasks. Sample functions string2type( ) and type2string( ) can solve a good range of formatting problems when transforming data from C++ types to plain text and vice versa.

Implementation of string2type( ) and type2string( ) functions simplifies string conversions by hiding low-level string stream manipulations, while providing relatively simple interface to potentially messy (if written from scratch) conversion procedures.

String-to-type conversion is able to signal a bad format, thus helping us to implement a simple validation check for protection against unwanted inputs.

On the other hand, type-to-string conversion has no validation, because its implementation assumes that conversion from internal data to external text is a safer operation.