|
|
Summary
30) Streams
31) Source and Destination of streams:
32) Formatted Input and Output
33) Recap Streams
34) Buffered Input/Output
35) Methods with streams
36) Examples using streams
Streams
We have been discussing the concept of ‘Streams’ from the very beginning of the
course.
In this lecture, various aspects of this concept will be discussed. There are two
types of
streams i.e. input streams and output streams. Before going into minute details,
we will
see what actually these two types are. You are well aware of the terms ‘cin’ and
‘cout’,
used several times in our previous programs. We have used
cout for output and
cin for
input. Similarly, the terms of file input and file output are very much known to
us. We
leaned how to write in files and how to read from files. These are also streams.
Let’s have
a look on the things and functions we can do with streams. There are some specific
functions for input and output in C.
printf and
scanf are normally used
in C. In these
functions, we have to tell what type of data we are using and in which variable.
Streams
are counterpart of this in C++. The input output system in C++ is streams. As the
name
applies, it’s a stream of bytes. As told earlier, it may also be termed as a door
through
which program can communicate with the outside world. For entering some data, we
use
cin stream while the data is read from the keyboard and stored in some
variable. To
display data on the screen, we can take help of
cout.
Page 447
For making the things more comprehensive, we will consider the example of a classroom.
Suppose you are sitting in the class and listening to this lecture. All of a sudden,
the students are asked by the instructor to go out of the classroom. How would you
do it?
You will get up from the seat and walk through the door. But you can see that all
of you
cannot go through the door simultaneously. You would go through the door one by
one.
Similarly if you have to come to the classroom, you will enter one by one and sit
on the
seats. In a way you are forming a sequence of people in this case. Stream is a sequence
of
bytes. It is an ordered sequence. Let’s compare it with the door example. The person
who
enters first will go out of the door first. The person who enters behind someone
will go
out behind that person. Similarly streams are also ordered sequence. The thing that
enters
first into the stream will go out first. You should think streams as ordered sequence
of
bytes. Byte is a unit of measure. A byte can store one character, so you can think
of an
ordered sequence of characters.
As programmers, we communicate with our programs in English through the keyboard.
We may be typing the letters, abc or the numbers, 012 on the keyboard. These all
are the
characters. In the programs, we store these characters in variables of different
data types.
Sometimes, these may be in some of our objects. On the keyboard, we type the character
‘a’ that is stored in some variable
c in our program. How these
two are linked? This link
is formed in cin
stream. Consider cin
as a pipe or a door. The character ‘a’ is entered
from one side and then a conversion takes place i.e. character is converted into
its binary
representation, stored in the character variable named
c. So there is an implicit
conversion happening. The same thing happens, if we have an integer
i and press the key
1 from the keyboard. The digit 1 travels as a character but inside it is stored
as number.
You have to be careful while dealing with this concept. We have talked about the
ASCII
characters. On the keyboard, we have alphabets, numbers and symbols. When you press
the number key from the keyboard, it goes at number inside the computer. In general
terms, it is a character. It means that when you enter a key on your keyboard, a
character
sequence is generated that later goes into the computer. This character sequence
has some
binary representation. It does not mean a sequence of characters, but a code that
goes
inside the computer on pressing a key on the keyboard. This code is called as ASCII
code. It is the binary representation of a character. Here both the characters ‘A’
and ‘B’
have some binary representation. Similarly ‘0’,’1’,’2’ have some binary representation.
It
does not mean that the character representation of ‘1’ is also ‘1’. If you are aware
of the
ASCII table (you have already written a program to display the ASCII table), it
will be
evident that the binary representation of character ‘1’ is some other value. Similarly
all
the numbers 1, 2, 3 etc have some ASCII value. So whenever you press any key on
the
keyboard, its ASCII code goes inside the computer.
Now when we use cin
stream to read some number from the keyboard and store it in the
integer variable, its binary representation is ignored and the value is stored.
So cin is
performing this transformation operation. It is taking a character ASCII code and
knows
that it is supposed to represent some number. It has the ability to convert it into
the
appropriate number before putting it into the integer variable
i. What happen if we use
cin
to read some value and store it in some integer variable and press some alphabet
key
Page 448
instead of numeric keys. Some error will occur. So in the
cin stream, an error can
be
detected.
Let’s us look at the collection of input and output classes. These are objects having
functions. This collection of classes and their functions are known as input-output
streams in the C++. The most common things that we have been using are
cin, used to get
input from the keyboard and
cout employed to display something on the screen. So one is
input stream and the other one is output stream. Since we are not going to discuss
the
object- oriented programming in this course. We will not discuss the hierarchy through
which these classes are derived. We have some objects for input stream and out put
stream e.g. cin
and cout respectively.
Being objects, they have member methods that we
can call. They also have the operators like ‘<<’ and ‘>>’, as used in the earlier
programs.
These operators ‘<<’, ‘>>’ are heavily overloaded. What does this mean? If we write
cin
>> i; here i
is an integer. Automatically
cin will take a character
in ASCII code from the
keyboard, convert it into number and store it into the integer variable. On the
other hand,
if we write cin >> c;
where c is
a character data type. When we press a key from the
keyboard, it will be stored in
c as a character. So the
stream extractor operator of cin (i.e.
>>, which gets the data from the stream and stores it into the variable) is already
overloaded. It knows how to behave with int, char, float etc data type and what
sort of
conversion is required. In case of float number, we have decimal point,
cin knows how to
treat it besides converting and storing it into a float variable. Similarly if we
use any
character pointer i.e. string, the same >> operator has the capability of reading
strings
too. Obviously, one operator can’t perform all these functions. It seems that we
are using
the same operator. Internally, this operator is overloaded. It is not the same for
int, char,
float, string and so on. But due to the operator overloading, its usage is very
simple. We
just write cin >> i;
it works perfectly.
Source and Destination of streams
As earlier said that streams are sort of door or pipe between two things. What are
these
two things? For every stream, there must be some source and some destination. For
cin,
the source is normally keyboard and the destination can be an ordinary variable
i.e.
native-data type variable. It could be some area of memory or our own data type,
i.e.
object for which we have overloaded the operator and so on. So always there is a
source
and there is a destination.
cout is output stream. It takes the data from the program and presents
it in human
readable form. It also has some source and destination. The source may be some file,
or
the region in memory or the processor or a simple variable or our own object of
our data
type. The destination is normally screen. The destination can be a file, screen,
or printer
etc. You have used file input and file output so you know how it works. When we
talk
about area in memory, it may be an array that we read or write. It could also be
a
character string which is itself an area in the memory.
“Every stream has an associated source and a destination”
Page 449
Now we will talk about yet another concept i.e. the state of stream. What does it
mean?
We say that cin >> i
; where i
is an integer. When we give it the input ‘a’ and press the
key ‘enter’, the stream knows that it is a bad input. So it is capable of signaling
and
setting its state specifying that some thing not good has been done. So from a program,
we can always test whether the state of stream is right or not. We should carry
out all
kinds of error checking, debugging and error handling while writing programs. We
don’t
want to manipulate bad data. So checking for this everywhere will be good. For example,
if we write a simple program that takes two integers from key-board, divides one
number
by the other and displays the result. Following will be the code segment.
int i, j ;
cin >> i ;
cin >> j ;
cout << i / j ;
Now we have to see what happens if the user gives a value 0 for j. We don’t want
to
divide it by zero as even the computer does not know how to do it. When we have
zero in
j, our program probably will work through an exception or error before coming to
a halt.
If we trap this error inside the program, it would be much nicer. We can say if
j is not
zero, it will be good to carry out the division. So error checking and handling
is always
important. The same thing applies to I/O streams. When we execute input or output
operation, we should check whether the operation has been carried out correctly
or not.
To do this, we can check the state of the stream.
Here is a simple example showing the simple use of streams.
/* Avoiding a precedence problem between the stream-insertion operator and the
conditional operator. */
#include<iostream>
int main()
{
int x,y;
cout<< "Enter two integers: ";
cin>>x>>y;
cout<<x << (x ==y ? " is" : " is not") <<" equal to "<< y;
return 0;
}
The output of the program:
Enter two integers: 3 3
3 is equal to 3
Page 450
Formatted Input and Output
Other things that the streams provide us are a capability of formatted input and
output.
We have been using cin
and cout
very simply without any formatting. As the output of a
program, we do not want that numbers should be printed in a way that makes it difficult
to read and understand. We want to format the output in a way that the numbers are
placed correctly at the correct position on the paper. You might have seen the electricity
bills or telephone bills printed by the computers. First the empty bills are printed
in the
printing press containing headings with blank boxes to put in the bill entries.
Then the
computer prints the entries in these boxes from the system. These entries (data
regarding
the bill) are printed at correct places on the bill. Sometimes, you see that the
entries are
properly printed. That is not a computer-fault but only due to poor paper adjustment
in
the printer. The printing of these entries is carried out by with the use of formatted
output.
The second example is the display of a matrix on the screen. Suppose, we want that
the
numbers of a column are displayed up and down in a column. Similarly the second
column should be displayed and so on. At first, we will format these things.
When we do word processing, we type a paragraph. The lines of the paragraph are
left
justified but ragged on right hand side. In word processing, we have a choice to
justify
the paragraph. By doing this, the left and right margins of the paragraph are put
in a
straight line while adjusting the space between the words. Now look what happens
if we
want to print a string with
cout and want it left or right justified. This is what we call
formatting the output. Similarly, we want to print the value of
pi which is stored in a
variable as 3.1415926. But we want that it should be printed as 3.141 that means
up to
three decimal places.
There should be a method of formatting it. Thus by formatting the output, the presented
representation (which we read as human being) can be different from the internal
representation. So we can do a lot of formatting with these streams.
Besides, there are member functions with the streams. Let’s look at the member functions
of cin. The first
one is the get function.
We can use it by writing:
cin.get();
The notation explains that
cin is an object (cin is an object of input stream) and
get is a
member function of it. This function reads a character. In this case, it reads a
single
character from key board and returns it as
cin is calling it. We have
two variants of this
get function with cin.
One is that cin.get
returns a character. W e can write it as
under:
c = cin.get() ;
The second method is cin.get(character
variable) i.e. one character at a time. It works
with characters, not through number or string. It is one character at a time.
Page 451
The second function of cin
is the read
function. This function differs from the
get
function in the way that it returns a buffer instead of a single character. So we
can point
to a buffer and tell the number of characters to be read. We normally provide a
delimiter,
a specific character up to which we want to read. Normally we use the new line character
as a delimiter and read a single line at a time.
Thus, we have three ways of obtaining input with
cin, which is an object
of type input
stream (istream). If we create an object of type
istream, it will also get
these functions as
it is derived from the same class.
We have seen that there are many methods and operators associated with
cin. The same
thing applies to cout.
cout is the output
stream which usually, presents the data from the
computer in human readable form. The operator associated with
cout is the stream
insertion (<<). That means we insert the operator in the stream and the stream displays
the output on the screen. So the operator with cout is << and it displays the value
of the
data variable that we provide it after the << sign. Thus to display the value of
an integer
variable i, we can write cout
<< i ; If we want to format the output, it can also be done
here.
The cout has a function write
with it. This function can be used if we want to write a
chunk of data from the buffer. Similarly, to output a single character,
cout has the
function named put.
It can be written as:
cout.put(character variable) ;
Here, it will display the value of the character variable.
Recap streams
Streams are nothing but an ordered sequence of bytes.
They allow data to move from one part of the computer to another which may be the
screen or key board from and to, or from memory or files on disc and so on.
Byte stream is used to connect the source and the destination.
These byte streams are implemented as objects. Being objects, they do have their
member
functions and have their member operators. The member operators are heavily
overloaded to allow these streams to handle a variety of data types.
The streams have a state that can be checked by us. We have used eof (end of file)
with
the file reading. This is a way to check the state of the stream.
While using I/O streams, we have to include some header files. Whenever we use
cin and
cout, the file iostream.h,
is included in which all these classes and objects have been
defined. For the formatted input and output, we manipulate the streams. To do stream
manipulations, we have to include a header file having the name
iomanip.h. We can
understand that iomanip
is a short hand for input output manipulation.
Page 452
Now let’s take a look at the standard streams which are provided to our programs.
Whenever we write a C++ program and include
iostream.h in it, we get
a stream for input
(reading) that is cin.
This is a built in thing. We can use this object. Similarly, for output
(writing), we get cout.
Other than these, we get some other streams by default. These
include cerr (read
as c error) and clog. To understand these streams, we have to talk
about buffered input and output.
Buffered Input/Output
In computers, most of the components relate to electronics like chips, memory, micro
processor etc. There are also electro-mechanical things like disc. The key board
itself is
an electro mechanical accessory. The electro mechanical parts of the computer are
normally very slow as compared to the electronic components. So there is a difference
between the two in terms of speed. Secondly, every input/output operation costs
computer time. Input/output costs and the I/O devices (keyboard, monitor and disc
etc)
are slower as compared to the speed of the microprocessor and the memory being used.
To overcome this speed difference, we use the mechanism, called buffered input/output.
Suppose, we have a program which executes a loop. That loop outputs a number in
each
of iteration to store in a file on the disc. If we write the output number to the
disc in each
iteration, it will be the horrendously wastage of computer time. It means that the
disc is
electro mechanical device. Similarly in each iteration, the mechanical movement
takes
time. But if we gather the data and write it to the disc, there will be one mechanical
movement. The heads of the disc will move mechanically once to a point and the whole
chunk of data will be written on the disc. This is the more efficient way of using
the disc.
So whenever we have a program that writes the output data to the disc, it will be
nice to
collect the output data (numbers) and write it on the disc in one write operation
instead of
writing the numbers one by one. The area where we gather the numbers is known as
buffer. The example stated in this case is the buffered output. In this case, the
output does
not go directly to the disc. We first gather the data in a buffer and then write
it on the
disc.
Now think about another situation. Suppose we have a program that performs very
complex calculations. It means that there is a
while loop that performs
so heavy
calculations that each of the iteration takes, say one minute and then provides
the result.
Now we want to write that output to a file on the disc and see the iteration number
of the
loop on the screen. We do not want to write the output to the disc after each iteration.
We
gather the data in a buffer. In the meantime, we want to see the loop counter on
the
screen. If we gather this output of counter number in a buffer, it may happen that
the
buffer gathers the iteration numbers for 250 iterations before displaying it on
the screen.
Thus, we see on the screen the numbers 1, 2, 3 …..250, when 250 iterations have
been
performed. There are again 250 numbers gathered at one time. We see numbers 251,
252
……500, when 500 iterations have been performed. When we start the program, there
will be two buffers gathering data. One buffer gathers the data to write to the
disc and the
other gets the data of iteration numbers to display on the screen. As we said that
each
iteration takes one minute, meaning that the iteration numbers will not be seen
for a long
time. Rather, these will be shown after 250 iterations (i.e. 250 minutes). During
this
Page 453
period, we do not show any thing on the screen. Here, we are not sure whether the
program is executing properly as we do not see any iteration number on the screen.
We
want to know after every minute that loop has executed once. So after every minute,
the
loop counter should be displayed on the screen, so that at any time we could see
how
many iterations have been performed. For this, we need unbufffered output on the
screen.
Thus, in the same program we require buffered and unbuffered output.
Now these requirements are contradictory with different issues. These are met in
our
system with the cerr
object. cout
is a buffered output. We cannot see it as nowadays
compilers are very intelligent. If you know UNIX, or command prompt and input output
redirection, we can actually see this in operation. Perhaps, you can create an example
to
understand this. For the moment just try to understand that
cout is buffered output.
It
gathers data and sends it to the screen. On the other hand,
cerr is unbuffered output.
It
will show the data on the screen at the same time when it gets it. So
cerr is an output
stream, an ostream
object but unbuffered. It shows data immediately. So we can use
something like the cerr
object that will show how many loops have been executed. It will
use something like cout
to write on the disk to buffer the output. In this case, we are
getting efficiency in addition to information. Normally we use
cerr object in C++ for
this
purpose. Besides, we also have
clog. It is also known
as standard log having detailed
information of the log. To collect information of the program, we write it with
clog.
Normally when we execute our programs-
cout, cerr and
clog, all are connected
to
screen. We have ways to direct them at different destinations. That depends on the
operating system. Suppose, we specify the buffer size, normally the operating system
or
compiler does this for us. A typical size of buffer is 512 bytes. When the information
is
of 512 byte size, output will take place. But in the program, we may want at some
point
that whatever is in the buffer, show them. Is there a way of doing that? The normal
mechanism is flush. Flush the stream. The flush command forces the data from the
buffer
to go to its destination which is normally a screen or file and make the buffer
empty.
Uptil now, we have been using two things to end the line. One is new line character
i.e.
“\n”. When we are displaying something on the screen, it makes the next output to
start
from the next line. The cursor moves to the left margin of the next line on the
screen.
New line is just a character. The other one was
endl. If we write
cout << endl; It
seems
that the same thing happens i.e. the cursor moves to the left margin of the new
line. But
endl actually does something else. It flushes the output too. As a result,
it seems that cout
is unbuffered i.e. its output is immediately available on the screen. Depending
on the
compiler and operating system, you may or may not see the buffered effect. But one
thing
is clear that while reading the source code, you will know where
cout is used and where
cerr. Typically, that is also the programming style where the output
of cerr is informative
and small. It shows that where is the control in the program. The output of
cout is more
detailed and the actual output of the program. There are benefits of these things
in the
code. In case of cin,
it is alone. For output, we have
cout, cerr and
clog. In DOS, we have
two more output streams i.e.
caux (auxiliary input output stream) and
cprn (printer
output). These are no more relevant now.
Predefined Stream Objects:
Page 454
Object Meaning
cin Standard input
cout Standard output
cerr Standard error with unbuffered output.
clog Standard error with buffered output
caux Auxiliary (DOS only)
cprn Printer(DOS only)
Now let’s take a look at the operators associated with these streams. We have been
using
the stream insertion operators ‘<<’ with
cout. We need to understand
that how these
operators are implemented. Using
cout, we can chained the
output. It means that we can
write as cout << “The value
of the first integer is “ << i; This is the single
cout
statement. How does that work? What happens is the first part goes to
cout. In this case, it
is the string “The value of the first integer is”. The data travels in the direction
of the
arrows. This string is inserted in the stream and displayed on the screen. What
about the
rest of the statement i.e.
<< i; this again needs
cout on the left side to
be executed. It
should look like as cout <<
i; Once, we have defined that this is the behavior expected
by us. Then, we understand that this is exactly the way it has been programmed.
The
stream insertion operator ‘<<’ is overloaded for the output stream and it returns
the
reference of the output stream. The syntax of stream insertion operator is:
ostream& ostream::operator << (char *text);
The important thing to note is that this operator returns the reference to the
ostream
object itself. Whenever we write a chained output statement, it is executed from
left to
right. So cout << “ The value
of the first integer is” is processed first from left to right.
The process is that this character string is displayed on the screen. As per the
prototype
and definition, it returns the reference to the
ostream object. In this
case, the object was
cout, so a reference to the
cout object is returned.
Now the rest of the statement becomes
as cout << i; It
is processed quite nicely. This allows the stream insertions to be chained.
It is also applicable to the input. So if we say something like
cin >> i >> j; both
i and
j
are integers. Now again, it is processed from left to right. This is the
istream and the
extraction operator will return the reference to the
istream object i.e.
cin. So at first,
the
cin >> i is processed which will return the reference to the
cin object. The rest of
the
statement seems as cin << j;
It is important to understand how these operators work. You
can see their prototypes that they return
iostream objects themselves.
That is the why, we
can chain them. Now let’s see what are the other various methods associated with
these
input output streams.
Methods of streams
There are some other functions associated with
cin stream. We have used
some of them.
We have used get()
and read()
methods with input stream. Another member function of
cin is getline().
It reads a complete buffer i.e. the number of character specified up to a
delimiter we specify. We can write something like:
cin.getline(char *buffer, int buff_size, char delimiter = ‘\n’)
Page 455
The character data is stored in
*buffer.
buff_size represents the
number of characters to be
read. If we specify its value 100, then
getline will read 99 characters
from the keyboard
and insert a null character in the end. As you know, in C++ every character string
ends
with a null character. We can also give it a delimiter. Sometimes, we may want to
read
less character. Normally, the delimiter is the new line character. So while typing
on the
keyboard, if we press the enter key then it should stop reading further and put
the data
into the variable buffer.
So there is a getline
fiunction.
There are some other interesting functions also. When we use
cin.get(), a character
is
read. We can throw back the character gotten by this
get function by using the
unget()
function. So we can use cin.unget()
that will return the most recently (last) gotten single
character.
We have a function peek(),
also written as cin.peek();
The purpose of this function is that
we can see the next character that we are going to get. This function returns the
next
character that would be read if we issue
cin.get().
All these functions (getline, get, read, unget and peek) are implemented as member
functions of the input class.
Similarly, there are functions associated with
cout. We have
cout.putline(); which outputs
a buffer. Actually we have no need of this function because
cout, itself, knows how
to
handle character strings. Then we have
cout.write(); which can
perform a raw,
unformatted output. The function
cout.put(); is like a formatted
output. It performs
character by character output. We can do many formatting conversions by using the
stream insertion operator (i.e. <<) with
cout. We can write an overloaded
function of
stream insertion (<<) to input or output a complex number. We know that a complex
number has two parts i.e. real and imaginary. We can write the overloaded function
such
that if we give two numbers with space between them it could read it. We can also
write
it as that it could read two numbers (that are real and imaginary parts of a complex
number) separated by comma. Thus there may be different ways to write the overloaded
operator.
The white space character is very significant. We can show it by a simple example.
Suppose we have an array name
of 60 characters. We get a name from the user in this
array by using cin
and then display this string by
cout. The code segment
for this purpose
can be written as:
char name [60] ;
cin >> name ;
cout << name ;
Now when the user enters the name, suppose it enters ‘naveed malik’ that is a name
containing two words with a space between them. When we display this name by using
cout, only ‘naveed’ is displayed on the screen. It means that only one
word ‘naveed’ was
Page 456
stored in the array. The reason for it that the streams (cin, cout) are sensitive
to white
space character that is treated as a delimiter. Now where is the second word ‘malik’.
It
has not got deleted yet. It is in the buffer of the stream. This example will read
like the
following:
char nam1 [30], name2 [30] ;
cin >> name1 >> name2 ;
Thus, we have two character arrays now. We can write ‘naveed malik’ and press enter.
The first part before space (naveed) will go to the first array
name1 when that array is
used with cin. We
will write another cin
with name2
and the second part (malik) will go
to the second array name2.
So things don’t disappear. They stay in the buffer till you
actually expect them. We have to be careful about that.
Examples using streams
A simple example showing the use of getline function.
// A simple example showing the use of getline function.
#include <iostream.h>
int main()
{
const int SIZE = 80;
char buffer[SIZE];
cout << " \n Enter a sentence: \n" ;
cin.getline(buffer, SIZE);
cout << " The sentence entered is: \n" << buffer << endl;
return 0;
}
Output of the program.
Enter a sentence:
this is a test
The sentence entered is:
this is a test
A simple example showing the use of read and write functions.
// A simple example showing the use of read and write functions.
#include <iostream.h>
int main()
{
const int SIZE = 80;
Page 457
char buffer[SIZE];
cout << " \n Enter a sentence: \n" ;
cin.read(buffer, 20);
cout << " The sentence entered was: \n";
cout.write(buffer, cin.gcount());
cout << endl;
return 0;
}
Output of the program.
Enter a sentence:
This is a sample program using read and write functions
The sentence entered was:
This is a sample pro |
|
|
|