Introduction to C++ Streams
The I/O operations provided in C standard library primarily consist of manipulating a buffer. The read and write functions provided by C standard library operate upon a buffer or a memory segment where the standard library can read from or write to. C++ enhances the functionality of buffers to streams.
C++ views the input and output data as a stream of characters. Each stream has a source and destination. The source inserts data into the stream. The source can be either a file, keyboard, or any other special device. The destination can retrieve some of the data from the stream and leave the rest of it in the stream to be retrieved later or to be retrieved by some other destination.
The source and destination of the streams are independet and they don't need to know about each other. The destination does not need to know how the source obtained the data or from where its coming from.
Though the C++ streams still use buffers but it is encapsulated in the stream functionality to such an extent that the user does not know about it.
Stream helper classes
C++ defines some template classes which implement the stream functionality.
However, C++ also provides specialization of these template classes which should be used. The specialized classes are defined in . Following are the specialized classes.
Standard stream class objects
The C++ standard provides some predefined stream objects that can be utilized in a C++ program. These stream objects can be accessed by including the iostream file. A quick look at the header file iostream
reveals the following definitions:
#include <bits/c++config.h>
#include <ostream>
#include <istream>
namespace std
{
... Edited for brevity ...
//@{
extern istream cin; ///< Linked to standard input
extern ostream cout; ///< Linked to standard output
extern ostream cerr; ///< Linked to standard error (unbuffered)
extern ostream clog; ///< Linked to standard error (buffered)
//@}
... Edited for brevity ...
} // namespace std
Some of the important stream objects included in this file are:
Using standard stream objects
In order to use create your own stream object or for using the standard stream objects, the stream files need to be included.
The standard stream object (i.e. cin, cout, cerr, clog) are included in iostream file. In older implemenattions of C++ these were present in the header file iostream.h, however in latest implementation if you attempt to include the iostream.h (or istream.h, ostream.h) you would get an error similar to the one below:
/usr/include/c++/4.2.1/backward/backward_warning.h:32:2: warning: #warning This file includes at least one deprecated or antiquated header. Please consider using one of the 32 headers found in section 17.4.1.2 of the C++ standard. Examples include substituting the <X> header for the <X.h> header for C++ includes, or <iostream> instead of the deprecated header <iostream.h>.
The reason for this warning is that the stream functionality is implemented in namespace now, so instead of including iostream.h you would have to include iostream (which contains the iostream implemented as a namespace.)
The iostream is implemented within std namespace, which means that cin needs to be referred as std::cin and cout needs to be referred as std::cout (same holds true for cerr and clog.)
Another thing to keep in mind is that if we fail to mention the std namespace we would get an error similar to the one below:
error: 'cout' was not declared in this scope
However, we can simply include "using namespace std;
" in our program in stead of prefixing every instance of standard stream objects with the std namespace.
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello World\n";
clog<<"Log: Hello World\n";
cerr<<"Error: Hello World\n";
return 0;
}
When we run the program we get the following output:
$ g++ stream.cc -o stream
$ ./stream
Hello World
Log: Hello World:
Error: Hello World
$
Difference between cout and cerr
cout is setup to send its output to standard output device, while cerr is setup to send its output to standard error device. Generally, standard output and standard error devices point to the monitor, but it is possible to change these devices to point to some other file or device using the shell's redirection operator.
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello World\n";
clog<<"Log: Hello World\n";
cerr<<"Error: Hello World\n";
return 0;
}
The normal output of this program would be
$ ./stream
Hello World
Log: Hello World
Error: Hello World
However, we can change the standard error device to a file (err in this case) using the shell's redirection operator.
$ ./stream 2>err Hello World
$ cat err
Log: Hello World
Error: Hello World
Difference between cerr and clog
Both of the cerr and clog point to standard error device. However, clog is buffered while cerr is not. This means that if something is written to the clog stream, it will not be written to the standard error device until the buffer is full or the cerr stream is flushed.
This translates in performance differences. Since cerr will be flushed everytime anything is written to it, cerr operations would be slow. If performance isn't paramount then cerr can be used to write the data to standard error device immediately, otherwise clog should be used This translates in performance differences. Since cerr will be flushed everytime anything is written to it, cerr operations would be slow. If performance isn't paramount then cerr can be used to write the data to standard error device immediately, otherwise clog should be used
Insertion operator
In the above program the weird characters "<<" are called as the the insertion operator. However the same operator is used in C to right left shift the bits. In C++ the insertion characters inserts an input data into a stream. By looking into the ostream header file we see that this wizardry is achieved by overloading the << operator in the ostream class.
// External global functions overloading the << operator
__ostream_type&
operator<<(__ostream_type& (*__pf)(__ostream_type&))
__ostream_type&
operator<<(__ios_type& (*__pf)(__ios_type&))
inline basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __out, _CharT __c)
inline basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, char __c)
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)
operator<<(basic_ostream<_CharT, _Traits>& __out, const _CharT* __s)
basic_ostream<_CharT, _Traits> &
operator<<(basic_ostream<_CharT, _Traits>& __out, const char* __s);
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, const char* __s)
inline basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, const signed char* __s)
inline basic_ostream<char, _Traits> &
operator<<(basic_ostream<char, _Traits>& __out, const unsigned char* __s)
// Internal functions overloading the << operator
__ostream_type&
operator<<(long __n);
__ostream_type&
operator<<(unsigned long __n);
__ostream_type&
operator<<(bool __n);
__ostream_type&
operator<<(short __n)
__ostream_type&
operator<<(unsigned short __n)
__ostream_type&
operator<<(int __n)
__ostream_type&
operator<<(unsigned int __n)
__ostream_type&
operator<<(double __f);
__ostream_type&
operator<<(float __f)
__ostream_type&
operator<<(long double __f);
__ostream_type&
operator<<(const void* __p);
__ostream_type&
operator<<(__streambuf_type* __sb);
In general the insertion operator has been overloaded for the following C++ basic types:
- char
- signed char
- unsigned char
- long
- unsigned long
- bool
- short
- unsigned short
- int
- unsigned int
- double
- float
- long double
- const void *
- const char *
- const signed char *
- const unsigned char *
This means that basic data types can be understood by the insertion operator. Therefore we can use insertion operator to insert normal variables into the standard output stream
unsigned in i = 10;
char c = 'a'; cout<<i; // Prints value 10
cout<<c; // Prints character a
The insertion operator has also been overloaded for pointers as well. Using pointers in form of const char *, const signed char *, or const unsigned char * will print the contents of the memory location until null character is encountered.
Using a const void * with insertion operator will cause the memory address to be printed.
Chaining muliple insertion operators
All the overloaded insertion oprators have __osteram_type &
as their return type. This means that all the insertion operators will return an object of ostream type after they have processed their input. The way insertion operators have been defined is that they return the same instance as the return value which was used to invoke them.
Therefore, an expression cout<<10
is a statement that does the following two things:
1. Puts 10 in the standrd output buffer, and then
2. Returns reference to cout object as its return value
This behaviour can be used to chain multiple insertion operators like following:
cout<<10<<20;
The above expression will be evaluated as cout.operator<<(10).operator<<(20);
When (cout.operator<<(10))
gets evaluated it will return the instance of cout which can again be used with the next insertion operator to print 20.
Other ostream output methods
In addition to the insertion operator, ostream class provides two other methods to write the data into the output stream.
ostream &put (char ch)
__ostream_type& put(char_type __c);
put method of the ostream class can be used to put a single character in the output stream. char ch = 'A'; cin.put(ch);
Since the put method returns ostream object so it can be chained similar to the extraction operator. char ch1 = 'A'; char ch2 = 'B'; cin.put(ch1).put(ch2);
ostream & write (const char *data, streamsize n)
__ostream_type& write(const char_type* __s, streamsize __n);
The write method can be used to write a specific number of bytes from a memory location. char *name = "John Smith"; cout.write(name, std::strlen(name));
Flushing the output buffer
If the stream is buffered such as in case of cout and clog, then the data in the stream will be put into the estination when the stream gets full.
In order to force the stream to send its data immediately to the destination, flush method can be called e.g. cout.flush();
Extraction operator
The extraction operator allows the data to be extracted from a stream to a destination. The insertion operator is used with cin stream object (which represents the standard input stream) to take an input from the user and direct its value to a destination variable. Consider the following code:
#include <iostream>
using namespace std;
int main()
{
int number;
cout<<"Enter a number \n";
cin>>number;
cout<<"The square of "<<number<<" is "<<(number*number)<<"\n";
return 0;
}
In the above code cin>>number will cause the program to wait for the user to input a value. Once the user enters a value (e.g. 42), its value will be stored in the variable number via the extraction operator.
The extraction operator has been overloaded in the istream header file.
// External functions overloading the << operator
basic_istream<_CharT, _Traits>&
operator>>(basic_istream<_CharT, _Traits>& __in, _CharT& __c);
inline basic_istream<char, _Traits>&
operator>>(basic_istream<char, _Traits>& __in, unsigned char& __c)
inline basic_istream<char, _Traits>&
operator>>(basic_istream<char, _Traits>& __in, signed char& __c)
basic_istream<char>&
operator>>(basic_istream<char>& __in, char* __s);
inline basic_istream<char, _Traits>&
operator>>(basic_istream<char, _Traits>& __in, unsigned char* __s)
inline basic_istream<char, _Traits>&
operator>>(basic_istream<char, _Traits>& __in, signed char* __s)
// Internal functions overloading the << operator
__istream_type&
operator>>(bool& __n)
__istream_type&
operator>>(short& __n);
__istream_type&
operator>>(unsigned short& __n)
__istream_type&
operator>>(int& __n);
__istream_type&
operator>>(unsigned int& __n)
__istream_type&
operator>>(long& __n)
__istream_type&
operator>>(unsigned long& __n)
__istream_type&
operator>>(float& __f)
__istream_type&
operator>>(double& __f)
__istream_type&
operator>>(long double& __f)
__istream_type&
operator>>(void*& __p)
__istream_type&
operator>>(__streambuf_type* __sb);
The extraction operator has been overloaded for the following C++ basic types:
- bool &
- short &
- unsigned short &
- int &
- unsigned int &
- long &
- unsigned long &
- float &
- double &
- long double &
- unsigned char &
- signed char &
- char *
- unsigned char *
- signed char *
- void *
Other than the pointers, the overloaded extraction operators accept the arguments as references so that they can store the value into the destination variable.
Chaining muliple extraction operators
Similar to the insertion operator, extraction operator has also returns the input istream instance as its return type. This allows the extraction operator to be chained together.
#include <iostream>
using namespace std;
int main()
{
int number1, number2;
cout<<"Enter two numbers \n";
cin>>number1>>number2;
cout<<"Product of "<<number1<<" and "<<number2<<" is "<<(number1*number2)<<"\n";
return 0;
}
$ g++ stream.cc -o stream
$ ./stream
Enter two numbers 10 20
Product of 10 and 20 is 200
In the above code cin chains two insertion operator to take input for two integers.
The expression cin>>number1>>number2;
is equivalent to cin.operator>>(number1).operator>>(number2);
Stream boundaries with extraction operator
Let us write a program which takes string input e.g. a name.
#include <iostream>
using namespace std;
int main()
{
char name[128];
cout<<"Enter your name \n";
cin>>name;
cout<<"The name entered is "<<name<<"\n";
return 0;
}
Let's execute this program with a input name such as "John Smith"
$ g++ stream.cc -o stream
$ ./stream
Enter your name John Smith
The name entered is John
What happened ? Where is smith ? Did the stream not take complete input ?
What has happened in this case is that the data input after "John" is still in the cin stream waiting to be extracted.
We can verify this by calling cin>>name
one more time.
#include <iostream>
using namespace std;
int main()
{
char name[128];
cout<<"Enter your name \n";
cin>>name;
cout<<"The name entered is "<<name<<" ";
cin>>name;
cout<<name<<"\n";
return 0;
}
$ g++ stream.cc -o stream
$ ./stream
Enter your name John Smith
The name entered is John Smith
Now the question that arises here is that why did the extraction operator leave the second word in the input stream.
In order to understand this behaviour we need to understand how the extraction operator works. When the extraction operator is asked to transfer data from the source (cin in this case) to a destination (name in this case), the extraction operator looks for a non-whitespace character in the input stream and then starts copying the data from that location to the destination. It stops when either of the following happens:
1. End of input is encountered
2. A failure is encountered
3. A white space is encountered
Consider the following code
#include <iostream>
using namespace std;
int main()
{
int number;
cout<<"Enter a number \n";
cin>>number;
cout<<"The square of "<<number<<" is "<<(number*number)<<"\n";
return 0;
}
$ g++ stream.cc -o stream
$ ./stream
Enter a number 10z1
The square of 10 is 100
Even though the entered data was incorrect, the extraction operator copied the first bytes which were interger and stopped at the invalid byte. This is an error condition and a program must detect a failure condition before it processes the data.
Other istream input methods
In addition to the insertion operator, ostream class provides two other methods to write the data into the output stream.
- int get(void)
- int_type get(); This function returns the next character from the input stream as its return value. If there is no character left in the input stream, it sets the failbit and returns EOF.
- istream & get(char &ch)
- __istream_type& get(char_type& __c); This function returns the next character from the input stream as refernce. If there is no character left in the input stream, it sets the failbit and returns EOF.
- istream & get(char *destination, streamsize n, char delim)
- __istream_type& get(char_type* __s, streamsize __n, char_type __delim); This function copies characters into the memory location pointed to by destination argument pointer. The characters are copied until one of the following happens:
- n-1 characters are stored
- input sequence reaches EOF
- next character equals delim character (which is passed as an argument to this function.) The delimiter is not copied.
- If no characters are stored, failbit is set for the stream.
- In any case, a null character is stored in the next location in the destination pointer.
- istream & get(char *destination, streamsize n)
- __istream_type& get(char_type* __s, streamsize __n) This function copies n number of bytes from the input stream into the destination pointer.
- istream & getline (char *destination, streamsize n, char delim) __istream_type& getline(char_type* __s, streamsize __n, char_type __delim); This function copies characters into the memory location pointed to by destination argument pointer. The characters are copied until one of the following happens:
- the input sequence reaches end-of-file, in which case eofbit is set
- the next character equals the delimiter character. The delimiter is extracted from the stream but not stored in the destination pointer
- n-1 characters are stored, in which case failbit is set
- istream & getline(char *destination, streamsize n) __istream_type& getline(char_type* __s, streamsize __n) This function copies n number of bytes from the input stream into the destination pointer.
- int peek() int_type peek(); Looks ahead and returns the next character in the stream without taking the character from the stream. If no character is present, this function returns EOF.
- istream & read(char *destination, streamsize n) __istream_type& read(char_type* __s, streamsize __n); This function extracts the data from the input stream and copies them in the destination pointer. This function will copy either n bytes from the buffer to the destination pointer. If the buffer does not have n bytes available then stream state os set to failbit|eofbit.
- streamsize readsome (char *destination, streamsize n) streamsize readsome(char_type* __s, streamsize __n); This function extracts the data from the input stream and copies them in the destination pointer. This function will copy either n bytes from the buffer to the destination pointer or until the buffer is exhausted.
- After the data is extracted, this function returns the number of bytes extracted.
- istream & ignore()
- __istream_type& ignore(); removes the next character from the input stream
- istream & ignore(streamsize n)
- __istream_type& ignore(streamsize __n); removes characters from the input stream until one of the following happens:
- n characters have been removed
- stream buffer is exhausted
- istream & ignore(streamsize n, int delimiter)
- __istream_type& ignore(streamsize __n, int_type __delim); removes characters from the input stream until one of the following happens:
- n characters have been removed
- stream buffer is exhausted
- delimiter is encountered
Detecting failure
The ios_base class implements some methods and attributes for detecting errors on streams. These are inherited by istream and ostream classes, and therefore are available in cin and cout as well.
The three attributes that are available to detect errors are: badbit, eofbit, and failbit.
The three attributes that are available to detect errors are: badbit, eofbit, and failbit.
The ios_base
header file shows that these bits are defined in enumeration iostate
as a bit mask.
enum _Ios_Iostate
{
_S_goodbit = 0,
_S_badbit = 1L << 0,
_S_eofbit = 1L << 1,
_S_failbit = 1L << 2,
_S_ios_iostate_end = 1L << 16
};
However, instead of checking these bits individually, the ios_base class provides additional methods that should be used to check these states.
A quick way to check if everything is fine by calling the good() function and see if it returns 0.
If the good() function does not return 0, then eof(), bad(), or fail() function can be called to see if the exact reason of failure.
cin>>number;
if (!cin.good())
{
if (cin.eof())
cout<<"Invalid Input : EOF";
else if (cin.bad())
cout<<"Invalid Input : Stream corrupted";
else
cout<<"Bad input";
}
else
{
cout<<"You entered "<<number;
}
The ios base class overloads the operator void* and ! which allow the stream objects to be used as an expressions (returning boolean values.)
operator void*() const { return this->fail() ? 0 : const_cast<basic_ios*>(this); }
bool operator!() const { return this->fail(); }
Therefore is is valid to write if (cin>>number)
, since cin>>number
will return reference to cin object itself and it can be used in an expression to evaluate to a true or false value.
if (cin>>number)
{
if (cin.eof())
cout<<"Invalid Input : EOF";
else
if (cin.bad())
cout<<"Invalid Input : Stream corrupted";
else
cout<<"Bad input";
} else {
cout<<"You entered "<<number;
}
Clearing error state
One important thing to keep in mind once any of the error bit gets set for a stream, it needs to be cleared explicitly. Otherwise subsequent operations will fail.
#include <iostream>
using namespace std;
int main()
{
int number;
cout<<"Enter a number \n";
cin>>number;
cout<<" You entered "<<number<<"\n";
cout<<"Enter another number \n";
cin>>number;
cout<<" You entered "<<number<<"\n";
return 0;
}
Here there are two cin calls one after the other. If we enter a character string as an input for the first cin statement, then the next cin statement will also fail even without taking any input from the user.
Enter a number qqqq
You entered 0
Enter another number
You entered 0
The stream state needs to be cleared using clear() method.
However, there is still one issue. There is still invalid data in the stream which needs to be flushed out.
One quick way of doing that is to retrive data from the input stream until a whitespace is found.
while(!isspace(cin.get())) continue;
Therefore the above program needs to be modified as following:
#include <iostream>
using namespace std;
int main()
{
int number;
cout<<"Enter a number \n";
cin>>number; cout<<" You entered "<<number<<"\n";
if (!cin)
{
cin.clear();
while(!isspace(cin.get()))
continue;
}
cout<<"Enter another number \n";
cin>>number;
cout<<" You entered "<<number<<"\n";
return 0;
}
The above code relies on the fact for an invalid input cin.good will return a non-zero value. However, there are cases where there is bad input but cin.good returns succeeds but the next extraction operation will fail.
e.g. if we enter 10z
as the integer, then cin.good() will succeed and the extraction operation will yeld 10. The cin input stream will contain z and when the next number is extracted, the cin>> operation will fail.
This can be avoided by peeking ahead into the input stream. If the stream is not empty then it means that some data has been left over by previous extraction operation.
#include <iostream>
#include <streambuf>
using namespace std;
int main()
{
int number;
cout<<"Enter a number \n";
cin>>number;
if (!cin)
{
cin.clear();
while(!isspace(cin.get()))
continue;
} else {
if (cin.peek() != EOF)
{
while(cin.peek() != EOF)
cin.ignore(); // Ignore the rest of the stream
} else {
cout<<" You entered "<<number<<endl;
}
}
cout<<"Enter another number \n";
cin>>number;
cout<<" You entered "<<number<<endl;
return 0;
}
get vs getline vs read
One behaviour that separates get, getline, and read family of functions is how they interact with the terminating character.
get and getline will stop extraction when they encounter the termiator character.
- get stops on a delimiter but does not remove the delimiter from the stream Consider the following program which takes input from cin stream using the get function one line at a time until an error is encountered.
#include <iostream>
using namespace std;
int main()
{
char buf[256];
while (cin)
{
cin.get(buf, 256);
cout<<"You entered "<<buf<<"\n";
}
return 0;
}
The output of the program for input of "Hello" string would be
Hello
You entered Hello
The loop failed to take correct input on the second iteration because the input stream still had the terminating character from the previous get call. The terminating character from the input stream can be extracted with the call to ignore method.
#include <iostream>
using namespace std;
int main()
{
char buf[256];
while (cin)
{
if(cin.get(buf, 256))
{
cin.ignore();
} cout<<"You entered "<<buf<<"\n";
}
return 0;
}
The output for the program would be
Hello
You entered Hello
World
You entered World
- getline family of functions stop on the terminating character and removes it from the stream as well. So there is no need to handle the stuck terminating character in the input stream.
- read family of function will extract the bytes and does not stop upon any terminating character.
formatted vs unformatted input/output
The insertion (>>) and extraction (<<) operators are known as formatted operators. Data can be formatted with them such as aligning the data, specifying fill.padding characters, changing the base of numbers, etc. They have pre-determined notions of delimiters and would convert the data to an expected type (if needed.) All this functionality is great but it can have impact on performance,
All the other operations such as get, getline, and read for extraction and put, write provide unformatted output. So, if performance is desired while reading from or writing to a stream then these unformatted functions should be used,