Skip to main content

Streams & Files

 

C++
 

The reason we titled this opening section as Stream I/O is because input/output operations in C++ occur in streams.  Streams are essentially a sequence of bytes that are sent from device to device.  A device can refer to an input device such as a keyboard, a disk drive or even across the network or output devices such as display screens, printers, etc.  

These streams are typically raw bytes but they have meaning within the context of an application.  Consider a Microsoft Word document being opened from disk.  If you attempt to open it using a generic text editor, you won't see the proper representation of the information stored in the document.  Word uses many different formatting characters and other information that specifies how the document should appear inside the Microsoft Word application.  Opening it in Microsoft Word, the stream of bytes now have the actual representation of the bytes and the text data and formatting data are properly separated.

 

Introduction of Streams

C++ defines a standard iostream library, which allows you to input and output data to the console using << and >> operators. We've seen some simple examples of these operators already. Here's another example, which asks the user to enter a couple of numbers and outputs their product:

    #include <iostream>
    …
    double unitPrice;
    int quantity;
    
    std::cout << "What is the unit price? ";
    std::cin >> unitPrice;
    
    std::cout << "How many do you want? ";
    std::cin >> quantity;
    
    std::cout << "Total cost is " << unitPrice * quantity << std::endl;

cin and cout are predefined objects in the std namespace. There's also a predefined object called cerr, which is handy for outputting error messages. These three objects are defined in the <iostream> header file. 

cin is an instance of the istream class, and allows you to perform input from the stdin device.
cout is an instance of the ostream class, and allows you to perform output to the stdout device. 
cerr is another instance of the ostream class, and allows you to perform output to the stderr device. 

We'll take a closer look at the istream and ostream classes in the next topic. 

**Note:** std::endl outputs a newline character to the output stream. Technically speaking, std::endl is a manipulator. We discuss manipulators later in this section.

A Closer Look at istream and ostream

istream and ostream aren't actually classes in their own right. Rather, they are typedefs (i.e. aliases) that represent character-based instantiations of the basic_istream and basic_ostream template classes respectively:

typedef basic_istream<char> istream;
typedef basic_ostream<char> ostream;

The basic_istream class defines a suite of operator >> functions that allow you to input values into built-in types. The following example shows how to input values for several built-in types:

    short s;
    std::cin >> s;
    
    int i;
    std::cin >> i;
    
    long l;
    std::cin >> l;
    
    unsigned short us;
    std::cin >> us;
    
    unsigned int ui;
    std::cin >> ui;
    
    unsigned long ul;
    std::cin >> ul;
    
    float f;
    std::cin >> f;
    
    double d;
    std::cin >> d;
    
    long double ld;
    std::cin >> ld;
    
    bool b;
    std::cin >> b;  // You must enter 0 or 1
    
    void * ptr;
    std::cin >> ptr;  // You can enter an address, e.g. FFFF

Similarly the basic_ostream class defines a suite of operator << functions that allow you to output built-in type values. For example, we can output all the variables from the previous code listing as follows:

    std::cout
        << s   << std::endl
        << i   << std::endl
        << l   << std::endl
        << ui  << std::endl
        << ul  << std::endl
        << f   << std::endl
        << d   << std::endl
        << ld  << std::endl
        << b   << std::endl
        << ptr << std::endl;
 

Inputting and Outputting Strings

The std::string class, defined in the <string> header file, defines >> and << operators that allow you to input and output strings to/from a stream. Here's a simple example:

    #include <iostream>
    #include <string>
    …
    std::string s;
    cin >> s;
    std::cout << "Your string is " << s << std::endl;

Note that the >> operator inputs a single word, i.e. it stops at the first whitespace character. If you want to input an entire line of text, you should use the std::getline() function instead. This function takes two parameters:

The input stream, from which you want to get the input
The std::string object, into which you want to store the value

The following example shows how to use std::getline(). The code snippet allows the user to enter his/her full name and address.

    std::string name;
    std::cout << "Full name: ";
    std::getline(std::cin, name);
    
    std::string address;
    std::cout << "Full address: ";
    std::getline(std::cin, address);
    
    std::cout 
        << name << std::endl
        << address << std::endl;
 

Extending Streams to Support Custom Classes

The istream and ostream classes support input and output for all the basic types, but obviously they don't have any inherent support for your own custom classes. For example, if you define a point class, then you can't automatically input and output point objects to a stream. The following code would give compiler errors:

    class point { … };
    point p1;
    std::cin >> p1;    // Compiler error, no operator >> function for point.
    std::cout << p1;   // Compiler error, no operator << function for point.

The good news is that you can easily define custom operator>> and operator<< functions to support stream input/output for custom classes. The techniques are idiomatic, i.e. once you've seen how to define operator>> and operator<< for one class type, then the syntax is basically the same for all other class types.

Let's see a complete example. Imagine we want to extend stream I/O to support the following simple point class (note the x and y data members are public here, for simplicity):

    class point
    {
    public:
        int x, y;
    
        // Plus other members, as appropriate.
    };

Given this class, you can define a custom operator>> function as follows:

    std::istream & operator >> (std::istream & is, point & p)
    {
        is >> p.x >> p.y;
        return is;
    }

Note the following points:

The function must be named operator >>. This is an example of operator overloading in C++, whereby you can extend the meaning of standard operators to support your own custom types.
The function must receive two parameters. The first parameter must be a std::istream reference, and represents the input stream from which the values will be obtained (e.g. std::cin). The second parameter is a point reference here, and represents the object that will be populated with input from the stream.
Inside the function, we use the istream object just like we'd use std::cin in normal code. Specifically, we get the x and y integer values separately.
The function must return a std::istream object by reference. This allows client code to use the >> operator in a cascaded fashion. For example, client code can ask the user to enter two point objects as follows:

        point p1, p2;
        std::cout << "Please enter two points: ";
        std::cin >> p1 >> p2;

In a similar way, you can define a custom operator<< function as follows:

    std::ostream & operator << (std::ostream & os, const point & p)
    {
        os << "[" << p.x << "," << p.y << "]";
        return os;
    }

Note the following points:
The function must be named operator <<.
The function must receive two parameters. The first parameter must be a std::ostream reference, and represents the output stream where values will be output (e.g. std::cout). The second parameter is a const point reference here, and represents the object that will be output to the stream.
Inside the function, we use the ostream just like we'd use std::cout in normal code. Specifically, we output the point in a format [x,y].
The function must return a std::ostream object by reference. This allows client code to use the << operator in a cascaded fashion. For example, client code can output two point objects as follows:

    std::cout
    << "Here are your points..." << std::endl
    << p1 << std::endl
    << p2 << std::endl;
 

Manipulators

You can format the way output is displayed on a stream. For example, you can specify the minimum width for a value when it's displayed, and whether the value should be left-aligned or right-aligned in that field width.

The way you control stream formatting is by using manipulators. Manipulators are predefined objects that you pass to the stream, to obtain the formatting effect you desire. The simplest example of a manipulator is std::endl, which tells the stream to output an end-of-line character:

    std::cout << "Hello world" << std::endl;

Another simple example is std::flush, which tells the stream to flush its internal buffer to the physical device (e.g. the console) immediately. This is useful if you are displaying debugging information, because it ensures your debug message is displayed immediately (e.g. before the program might crash later on):


    std::cout << "Here is a debug message, the value of x is " << x << std::flush;

Manipulators such as std::endl and std::flush don't take any parameters, and are defined in the <iostream> header file. There are other manipulators that do take parameters, and these are defined in the <iomanip> header file. A simple example is std::setw, which sets the minimum field width for the next value displayed on the stream. To use parameterized manipulators, you must include the <iomanip> header file in your code (as well as <iostream>, of course):

    #include <iomanip>// Necessary for parameterized manipulators.
    #include <iostream>   // Necessary for general stream I/O definitions.
    …
    std::cout << std::setw(10) << -123.45 << std::endl;

The following demonstrations show how to achieve these formatting effects:

Setting the field width
Justifying a value within a field width
Formatting floating-point values
Additional miscellaneous formatting techniques
 

Setting Field Width Demo

The std::setw() manipulator sets the minimum field width for the next value displayed. Here's a simple example, which displays a text value in a minimum field width of 10 characters:

    const char * message = "wibble";
    std::cout << "[" << std::setw(10) << message << "]" << std::endl;

Note that std::setw() only applies to the next value. The default minimum field width is 0 characters. std::setw() does not cause truncation – if the value exceeds the minimum field width, it will be displayed in full.

This demonstration illustrates the use of std::setw().
 

 

Output Formatting Demo

If you set a minimum field width for a value, the value is right-justified by default. To take control of justification, use the std::left and std::right manipulators as follows:

    std::cout << std::left << std::setw(10) << "hello" << "world" << std::endl;
    std::cout << std::right << std::setw(10) << "hello" << "world" << std::endl;

There's also a manipulator called std::internal, which justifies numeric values within the field width. The + or – sign is left-justified at the start of the field with, and the number itself is right-justified at the end of the field width:

    std::cout << std::internal << std::setw(10) << -123.45 << std::endl;

Note that std::left, std::right, and std::internal apply to all subsequent outputs. 
This demonstration illustrates the use of std::left, std::right, and std::internal.
 
 
 
 
C++ streams have three ways to format floating-point values:

Fixed format – always displays a number, a decimal point, and a fraction part (i.e. it never uses scientific format such as 1.23e5, regardless of how big or small the number is)
Scientific format – always uses scientific format such as 1.23e5
General format – uses fixed format if the number isn't too big or small, otherwise uses scientific format. This is the default format.

To take control of floating-point formatting, use the std::fixed and std::scientific manipulators. std::fixed and std::scientific apply to all subsequent outputs.  For example:

    double pi = 3.14159265358979;
    std::cout << std::fixed << pi << std::endl;
    std::cout << std::scientific << pi << std::endl;

There is no manipulator called std::general. Rather, if you want to re-establish the general formatting, you must call the unsetf() function upon the stream object, to unset the internal bit-fields that represent fixed and scientific formatting:

    std::cout.unsetf(std::ios::fixed | std::ios::scientific);

This demonstration illustrates how to format floating-point values.
 

Floating Point Format Demo


 There are several manipulators that allow you to fine-tune exactly how you want your values to be formatted. We explore these manipulators in this topic. All manipulators discussed here apply for all subsequent values passed to the stream.

The std::showpos and std::noshowpos manipulators allow you to specify whether you want the + sign to be displayed alongside positive numbers. The default is for the + sign not to be displayed. Here's a simple example:

    int x, y, z;
    std::cout << std::showpos;
    std::cout << x << " " << y << " " << z << std::endl;
    std::cout << std::noshowpos;
    std::cout << x << " " << y << " " << z << std::endl;

The std::dec, std::oct, and std::hex manipulators allow you to specify whether numbers should be formatted in decimal, octal, or hexadecimal. The default is decimal. The std::showbase manipulator is also quite handy, because it causes the number base to be displayed alongside numbers (i.e. a leading 0 for octal numbers, and a leading 0x for hexadecimal numbers). The std::noshowbase manipulator stops the number base from being displayed. Here's a simple example:

    std::cout << std::showbase;
    std::cout << 64 << std::endl;
    std::cout << std::oct << 64 << std::endl;
    std::cout << std::hex << 64 << std::endl;
    std::cout << std::noshowbase;

The std::uppercase manipulator causes numbers to be formatted with uppercase letters where necessary (i.e. A-F in hex numbers, X in the 0X prefix, and E for scientific formatting). The std::nouppercase manipulator stops letters being displayed in uppercase. Here's a simple example:

    std::cout << std::hex;
    std::cout << 123456789 << std::endl;
    std::cout << std::uppercase   << 123456789 << std::endl;
    std::cout << std::nouppercase << 123456789 << std::endl; 

This demonstration illustrates how to use the manipulators described in this topic.

Misc. Output Formatting Demo


 

 

 

 

 

 

 

 

 

 

Comments

Popular posts from this blog

Function and Objects

C++ Programming   Introducing to Functions Functions are a component of a programming language that permit you to break down the behavior of an application into discreet pieces of functionality, hence the name function.  A function is essentially a block of C++ code that you give a name and then call from other locations in your application, when you want the computer to perform the instructions contained in that function. You create a function by defining it.  The function definition may contain a return type, a function name, parameters to accept incoming arguments, and finally a body which contains the code that will execute as part of that function.  Functions may or may not return a value back to the caller and they may or may not accept arguments passed in to the function. When you move into more advanced C++ programming, you can also overload functions.  This refers to the practice of using the same function name to refer to multip...

C++ Classes

C++ Programming  Introduction to Splitting Class Files Class structure  If an aspect of object-oriented programming is encapsulation, then why would we split our classes into separate files?  Why not keep it all in one if we wish to "encapsulate" all there is about the class? Recall that a part of encapsulation is also data hiding.  Think about your car for a bit as we use it in an analogous way.  A car is a complex device that contains many different components.  You are able to operate your car without the need to understand the complexities of the internal combustion engine or the radio electronics.  You don't need that knowledge to be able to effectively operate the car. Likewise, a programmer using your class files does not need to know how you have implemented your methods to achieve the functionality.  In fact, perhaps you have a proprietary algorithm for a spell checker that is really fast and you don't want any...

Control Statements

C++  Programming  # C++ operators  Because you will start to learn about control statements in C++, it's important to understand the C++ operators that exist in the language first, as they play an important role in control statements. You will work with comparison operators to determine if values are equal, greater, or less than each other.  C++ also allows you to use mathematical operators for incrementing values to help control the number of iterations in a loop.  You can also make use of bitwise operators to speed up some operations in your code.  Operator Description + addition - subtraction * multiplication / division % modulo += (y += x) same as y = y + x -= (y -= x) same as y = y - x *= (y *= x) same as y = y * x ++ increment by 1 -- decrement by 1 == equal to != not equal to > greater than < less than >= greater than or equal to <= less than or equal to && logical AND || logical OR ! logical NOT ...