Friday 16 November 2012

The iostream Library in C++

Unit 3 The iostream Library
Structure
3.1 Introduction
3.2 Understanding the C++ iostream hierarchy
3.2.1 Standard Input/Output Stream Library
3.2.2 Organization
3.2.3 Elements of the iostream Library
Self Assessment Questions
3.3 Basic Programming using Streams
3.3.1 Basic Stream Concepts
3.3.2 Your Very First Program
3.3.3 Reading a File
3.4 Managing I/O Streams
3.4.1 Opening a File – Different Methods
3.4.2 Checking for Failure with File Commands
Self Assessment Questions
3.5 Checking the I/O Status - Flags
3.6 Dealing with Binary Files
3.7 Some Useful Functions
Self Assessment Questions
3.8 Manipulators
3.9 Summary
3.10 Terminal Questions
References and Further Reading
3.1 Introduction
In C++, I/O is done with "streams." An input stream such as cin is a source of data that can be read into variables. An output stream such as cout is a place where data can be sent for display, storage, or communication. A file is a collection of data saved on a disk drive. Data in a file differs from data in variables because a file can exist before the program is run and can persist after the program ends. To read data from a file or to save data in a file, a program just has to use the right type of stream. An object of type ifstream is an "input file stream" that can be used to read data from a file into variables. An object of type ofstream is an "output file stream" that can be used as a place to send data. The classes ifstream and ofstream are both defined in the library file named "fstream", so if you want to use files, you should "#include <fstream>" at the beginning of your program. This chapter will explain all of these and more in detail. So, read on.
Objectives:
Once you have gone through and understood the stream library described in this chapter, you will be able to:
· Understand the C++ iostream library and its organization.
· Do file I/O using streams.
· Understand how to deal with different kinds of files.
· Use manipulators in your programs.
· Make use of C++ streams in your program and also format your output using manipulators.
3.2 Understanding the C++ iostream Library
3.2.1 Standard Input/Output Stream Library
clip_image001
The iostream library is an object-oriented library that provides input and output functionality using streams. A stream is an abstraction that represents a device on which input and output operations are performed. A stream can basically be represented as a source or destination of characters of indefinite length.
Streams are generally associated to a physical source or destination of characters, like a disk file, the keyboard, or the console, so the characters we get or write to/from our abstraction called stream are physically input/output to the physical device. For example, file streams are C++ objects to manipulate and interact with files. Once a file stream is used to open a file, any input or output operation performed on that stream is physically reflected in the file.
To operate with streams we have to our disposal the standard iostream library which provides us the following elements:
· Basic class templates – The base of the iostream library is the hierarchy of class templates. The class templates provide most of the functionality of the library in a type-independent way. This is a set of class templates that have two template parameters: the char type (charT) paramter, that determines the type of elements to be manipulated and the traits parameter that provides additional characteristics specific for that type of elements. The class templates in this class hierarchy have the same name as their char-type instantiations but with the prefix basic_. For example, the class template which istream is instantiated from is called basic_istream, the one from which fstream is called basic_fstream, and so on. The only exception is ios_base, which is by itself type-independent.
· Class template instantiations – The library incorporates two standard sets of instantiations of the entire iostream class template hierarchy: one is narrow-oriented, to manipulate elements of type char and another one, wide-oriented, to manipulate elements of type wchar_t. The narrow-oriented (char type) instantiation is probably the better known part of the iostream library. Classes like ios, istream and ofstream are narrow-oriented. The diagram shows the names and relationships of narrow-oriented classes. The classes of the wide-oriented (wchar_t) instantiation follow the same naming conventions as the narrow-oriented instantiation but with the name of each class and object prefixed with a w character, forming wios, wistream and wofstream, as an example.
· Standard objects – As part of the iostream library, the header file <iostream> declares certain objects that are used to perform input and output operations on the standard input and output. They are divided in two sets: narrow-oriented objects, which are the popular cin, cout, cerr and clog and their wide-oriented counterparts, declared as wcin, wcout, wcerr and wclog.
· Types – The iostream classes barely use fundamental types on their member's prototypes. They generally use defined types that depend on the traits used in their instantiation. For the default char and wchar_t instantiations, types streampos, streamoff and streamsize are used to represent positions, offsets and sizes, respectively.
· Manipulators – Manipulators are global functions designed to be used in conjunction with insertion (<<) and extraction (>>) operators performed on iostream stream objects. They generally modify properties and formatting settings of the streams. endl, hex and scientific are some examples of manipulators.
3.2.2 Organization
The library and its hierarchy of classes are split in different files:
· <ios>, <istream>, <ostream>, <streambuf> and <iosfwd> aren't usually included directly in most C++ programs. They describe the base classes of the hierarchy and are automatically included by other header files of the library that contain derived classes.
· <iostream> declares the objects used to communicate through the standard input and output (including cin and cout).
· <fstream> defines the file stream classes (like the template basic_ifstream or the class ofstream) as well as the internal buffer objects used with these (basic_filebuf). These classes are used to manipulate files using streams.
· <sstream>: The classes defined in this file are used to manipulate STL string objects as if they were streams.
· <iomanip> declares some standard manipulators with parameters to be used with extraction and insertion operators to modify internal flags and formatting options.
3.2.3 Elements of the iostream Library
Classes
ios_base
Base class with type-independent members for the standard stream classes
ios_base
Base class with type-dependent members for the standard stream classes
istream
Input stream
ostream
Output Stream
iostream
Input/Output Stream
ifstream
Input file stream class
ofstream
Output file stream
fstream
Input/output file stream class
istringstream
Input string stream class
ostringstream
Output string stream class
stringstream
Input/output string stream class
streambuf
Base buffer class for streams
filebuf
File stream buffer
stringbuf
String stream buffer
Objects
cin
Standard input stream
cout
Standard output stream
cerr
Standard output stream for errors
clog
Standard output stream for logging
Types
fpos
Stream position class template
streamoff
Stream offset type
streampos
Stream position type
streamsize
Stream size type
Manipulators
boolalpha
Alphanumerical bool values
dec
Use decimal base
endl
Insert newline and flush
ends
Insert null character
fixed
Use fixed-point notation
flush
Flush stream buffer
hex
Use hexadecimal base
internal
Adjust field by inserting characters at an internal position
left
Adjust output to the left
noboolalpha
No alphanumerical bool values
noshowbase
Do not show numerical base prefixes
noshowpoint
Do not show decimal point
noshowpos
Do not show positive signs
noskipws
Do not skip whitespaces
nounitbuf
Do not force flushes after insertions
nouppercase
Do not generate upper case letters
oct
Use octal base
resetiosflags
Reset format flags
right
Adjust output to the right
scientific
Use scientific notation
setbase
Set basefield flag
setfill
Set fill character
setiosflags
Set format flags
setprecision
Set decimal precision
setw
Set field width
showbase
Show numerical base prefixes
showpoint
Show decimal point
showpos
Show positive signs
skipws
Skip whitespaces
unitbuf
Flush buffer after insertions
uppercase
Generate upper-case letters
ws
Extract whitespaces
Self Assessment Questions
1. Depict the iostream library with the help of an organization chart.
2. Describe the elements of the C++ iostream library.
3.3 Basic Programming using Streams
3.3.1 Basic Stream Concepts
In C++, the file stream classes are designed with the idea that a file should simply be viewed as a stream or array of uninterpreted bytes. For convenience, the "array" of bytes stored in a file is indexed from zero to len-1, where len is the total number of bytes in the entire file.
Each open file has two "positions" associated with it:
· The current reading position, which is the index of the next byte that will be read from the file. This is called the "get pointer" since it points to the next character that the basic get method will return.
· The current writing position, which is the index of the byte location where the next output byte will be placed. This is called the "put pointer" since it points to the location where the basic put method will place its parameter.
These two file positions are independent, and either one can point anywhere at all in the file.
Creating Streams
Before we can use an input or output stream in a program, we must "create" it. Statements to create streams look like variable declarations, and are usually placed at the top of programs or function implementations along with the variable declarations. So for example the statements
ifstream in_stream;
ofstream out_stream;
respectively create a stream called "in_stream" belonging to the class (like type) "ifstream" (input-file-stream), and a stream called "out_stream" belonging to the class "ofstream" (output-file-stream). However, the analogy between streams and ordinary variables (of type "int", "char", etc.) can't be taken too far. We cannot, for example, use simple assignment statements with streams (e.g. we can't just write "in_stream1 = in_stream2").
Connecting and Disconnecting Streams to Files
Consider that a file named “test.txt” exists. The diagrammatic representation of this file is shown below:
clip_image003
Fig. 2.1: test.txt
Having created a stream, we can connect it to a file using the member function "open(...)". The function "open(...)" has a different effect for ifstreams than for ofstreams (i.e. the function is polymorphic).
To connect the ifstream "in_stream" to the file "test.txt", we use the following statement:
                    in_stream.open("Test.txt");
This connects "in_stream" to the beginning of "test.txt". Diagrammatically, we end up in the following situation:
clip_image005
Fig. 2.2: opening input stream
To connect the ofstream "out_stream" to the file "test.txt", we use an analogous statement:
                    out_stream.open("Test.txt");
Although this connects "out_stream" to "test.txt", it also deletes the previous contents of the file, ready for new input. Diagrammatically, we end up as follows:
clip_image007
Fig. 2.3: opening output stream
To disconnect connect the ifstream "in_stream" to whatever file it is connected to, we write:
                    in_stream.close();
Diagrammatically, the situation changes from that of Figure 2.2 to:
clip_image009
Fig. 2.4: closing an input stream
The statement:
                    out_stream.close();
has a similar effect, but in addition the system will "clean up" by adding an "end-of-file" marker at the end of the file. Thus, if no data has been output to "Test.txt" since "out_stream" was connected to it, we change from the situation in Figure 2.3 to:
clip_image011
Fig. 2.5: closing an output stream
In this case, the file "Test.txt" still exists, but is empty.
Section 2.3.2 and 2.3.3 will aid in further understanding these concepts by providing examples that will illustrate the usage of streams. You will realize that working with streams is not as hard as it seems.
3.3.2 Your Very First Program
The first program, will create a file, and put some text into it.
#include <fstream>
using namespace std;
int main() {
ofstream SaveFile("cpp-home.txt");
SaveFile << "Hello World, from www.cpp-home.com and Loobian!";
SaveFile.close();
return 0;
}
This program will create the file cpp-home.txt in the directory from where you are executing it, and will put “Hello World, from www.cpp-home.com and Loobian!” into it.
Here is what every line means:
#include <fstream>
You need to include this file in order to use C++’s functions for File I/O.In this file, are declared several classes, including ifstream, ofstream and fstream, which are all derived from istream and ostream.
ofstream SaveFile(“cpp-home.txt”);
· ofstream means “output file stream”. It creates a handle for a stream to write in a file.
· SaveFile – that’s the name of the handle. You can pick whatever you want.
· (“cpp-home.txt”); - opens the file cpp-home.txt, which should be placed in the directory from where you execute the program. If such a file does not exist, it will be created for you.
ofstream is a class. So, ofstream SaveFile(“cpp-home.txt”); creates an object from this class. What we pass in the brackets, as parameter, is actually what we pass to the constructor. It is the name of the file. So, to summarize: we create an object from class ofstream, and we pass the name of the file we want to create, as an argument to the class’ constructor.
SaveFile << “Hello World, from www.cpp-home.com and Loobian!”;
<<” looks familiar. Yes, you’ve seen it in cout <<. This (“<<”) is a predefined operator. Anyway, what this line makes is to put the text above in the file. As mentioned before, SaveFile is a handle to the opened file stream. So, we write the handle name, << and after it we write the text in inverted commas. If we want to pass variables instead of text in inverted commas, just pass it as a regular use of the cout <<. For example, SaveFile << variablename;
SaveFile.close();
As we have opened the stream, when we finish using it, we have to close it. SaveFile is an object from class ofstream, and this class (ofstream) has a function that closes the stream. That is the close() function. So, we just write the name of the handle, dot and close(), in order to close the file stream! Once you have closed the file, you can’t access it anymore, until you open it again.
That’s the simplest program, to write in a file.
3.3.3 Reading a File
You saw how to write into a file. Now, when we have cpp-home.txt, we will read it, and display it on the screen. There are several ways to read a file. These will be discussed later in this chapter. But first let’s look at one of the simplest and a most effective technique.
#include <fstream.h>
void main() { //the program starts here
ifstream OpenFile("cpp-home.txt");
char ch;
while(!OpenFile.eof()) {
OpenFile.get(ch);
cout << ch;
}
OpenFile.close();
}
You should already know what the first line is. Hence, we shall move to next important statement.
ifstream OpenFile(“cpp-home.txt”);
ifstream means “input file stream”. In the previous program, it was ofstream, which means “output file stream”. The previous program is to write a file, that’s why it was “output”. But this program is to read from a file, that’s why it is “input”. OpenFile is the object from class ifstream, which will handle the input file stream. And in the inverted commas, is the name of the file to open.
Notice that that there is nothing to check whether the file exists. This will be covered in a short while.
char ch;
An explanation for this statement is redundant.
while(!OpenFile.eof())
The function eof() returns a nonzero value if the end of the file has been reached. So, we make a while loop, that will loop until we reach the end of the file. So, we will get through the whole file, so that we can read it.
OpenFile.get(ch);
OpenFile is the object from class ifstream. This class declares a function called get(). So, we can use this function, as long as we have an object. The get() function extracts a single character from the stream and returns it. In this example, the get() function takes just one parameter – the variable name, where to put the read character. So, after calling OpenFile.get(ch) it will read one character from the stream OpenFile, and will put this character into the variable ch. If you call this function for a second time, it will read the next character, but not the same one. That’s why, we loop until we reach the end of the file. And every time we loop, we read one character and put it into ch.
cout << ch;
Explanation for this statement is redundant.
OpenFile.close();
As we have opened the file stream, we need to close it. Use the close() function, to close it. Just as in the previous program.
When you compile and run this program, it should output:
Hello World, from www.cpp-home.com and Loobian!
3.4 Managing I/O Streams
In this section, we will discuss some useful functions. We will also see how to open file to read and write in the same time. We will also explore other ways to open a file; how to check if opening was successful or not.
3.4.1 Opening a File – Different Methods
So far we have seen just one way to open a file, either for reading, either for writing. But it can be opened another way too. So far, you should be aware of this method:
ifstream OpenFile(“cpp-home.txt”);
Well, this is not the only way. As mentioned before, the above code creates an object from class ifstream, and passes the name of the file to be opened to its constructor. But in fact, there are several overloaded constructors, which can take more than one parameter. Also, there is function open() that can do the same job. Here is an example of the above code, but using the open() function:
ifstream OpenFile;
penFile.open(“cpp-home.txt”);
“What is the difference?” one might ask. If you want to create a file handle, but don’t want to specify the file name immediately, you can specify it later with the function open(). Other use of open() is for example if you open a file, then close it, and using the same file handle open another file. This way, you will need the open() function.
Consider the following code example:
#include <fstream.h>
void read(ifstream &T) { //pass the file stream to the function
//the method to read a file
char ch;
while(!T.eof()) {
T.get(ch);
cout << ch;
}
cout << endl << "--------" << endl;
}
void main() {
ifstream T("file1.txt");
read(T);
T.close();
T.open("file2.txt");
read(T);
T.close();
}
So, as long as file1.txt and file2.txt exists and has some text into, you will see it.
It is time now to show you that the file name is not the only parameter that you can pass to the open() function or the constructor (it’s the same). Here is a prototype:
ifstream OpenFile(char *filename, int open_mode);
You should know that filename is the name of the file (a string). What is new here is the open_mode. The value of open_mode defines how to a file can be opened. Here is a table of the open modes:
ios::in
Open file to read
ios::out
Open file to write
ios::app
All the date you write, is put at the end of the file. It calls ios::out
ios::ate
All the date you write, is put at the end of the file. It does not call ios::out
ios::trunc
Deletes all previous content in the file. (empties the file)
ios::nocreate
If the file does not exists, opening it with the open() function gets impossible.
ios::noreplace
If the file exists, trying to open it with the open() function, returns an error.
ios::binary
Opens the file in binary mode.
In fact, all these values are int constants from an enumerated type. But for making your life easier, you can use them as you see them in the table. Here is an example on how to use the open modes:
#include <fstream.h>
void main() {
ofstream SaveFile("file1.txt", ios::ate);
SaveFile << "That's new!n";
SaveFile.close();
}
As you see in the table, using ios::ate will write at the end of the file. If it wasn’t used, the file would have been overwritten. So, if file1.txt has this text:
Hi! This is test from www.cpp-home.com!
Running the above code, will add “That’s new!” to it, so it will look this way:
Hi! This is test from www.cpp-home.com!That’s new!
If you want to set more than one open mode, just use the OR operator (|). This way:
ios::ate | ios::binary
Using different open modes helps make file handling an easy job. Having
the liberty to choose a combination of these, in a sane way, comes in very handy in using streams effectively, and to the requirements of the project.
Moving on to something more intriguing and important; we can create a file stream handle, which you can use to read/write file, in the same time. Here is how it works:
fstream File(“cpp-home.txt”, ios::in | ios::out);
In fact, that is only the declaration. The code line above creates a file stream handle, named File. As you know, this is an object from class fstream. When using fstream, you should specify ios::in and ios::out as open modes. This way, you can read from the file, and write in it, in the same time, without creating new file handles. Well, of course, you can only read or write. Here is the code example:
#include <fstream.h>
void main() {
fstream File("test.txt", ios::in | ios::out);
File << "Hi!"; //put “Hi!” in the file
static char str[10]; //when using static, the array is automatically
//initialized, and very cell NULLed
File.seekg(ios::beg); //get back to the beginning of the file
//this function is explained a bit later
File >> str;
cout << str << endl;
File.close();
}
Let us now understand the above program:
fstream File(“test.txt”, ios::in | ios::out);
This line, creates an object from class fstream. At the time of execution, the program opens the file test.txt in read/write mode. This means, that you can read from the file, and put data into it, at the same time.
File << “Hi!”;
I am sure the reader is aware of this and hence the explanation for this statement is redundant.
static char str[10];
This makes a char array with 10 cells. The word static initializes the array when at the time of creation.
File.seekg(ios::beg);
To understand this statement, let us go back and recollect some basics. We have seen this before:
while(!OpenFile.eof()) {
OpenFile.get(ch);
cout << ch;
}
This is a while loop, that will loop until you reach the end of the file. But how does the loop know if the end of the file is reached? The answer is; when you read the file, there is something like an inside-pointer (current reading/writing position) that shows where you are up to, with the reading (and writing, too). Every time you call OpenFile.get(ch) it returns the current character to the ch variable, and moves the inside-pointer one character after that, so that the next time this function is called, it will return the next character. And this repeats, until you reach the end of the file.
Going back to the code line; the function seekg() will put the inside-pointer to a specific place (specified by you). One can use:
· ios::beg – to put it in the beginning of the file
· ios::end – to put it at the end of the file
Or you can also set the number of characters to go back or after. For example, if you want to go 5 characters back, you should write:
File.seekg(-5);
If you want to go 40 characters after, just write:
File.seekg(40); It is imperative to mention that the seekg() function is overloaded, and it can take two parameters, too. The other version is this one:
File.seekg(-5, ios::end);
In this example, you will be able to read the last 4 characters of the text, because:
· You go to the end (ios::end)
· You go 5 characters before the end (-5)
Why you will read 4 but not 5 characters? One character is lost, because the last thing in the file is neither a character nor white space. It is just position (i.e., end of file).
Why this function was used the program above. After putting “Hi!” in the file, the inside-pointer was set after it, i.e., at the end of the file. And as we want to read the file, there is nothing that can be read at the end. Hence, we have to put the inside-pointer at the beginning. And that is exactly what this function does.
File >> str;
I believe this line reminds us of cin >>. In fact, it has much to do with it. This line reads one word from the file, and puts it into the specified array. For example, if the file has this text:
Hi! Do you know me?
Using File >> str, will put just “Hi!” to the str array. And, as what we put in the file was “Hi!” we don’t need to do a while loop, that takes more time to code. That’s why this technique was used. By the way, in the while loop for reading, that was used so far, the program reads the file, char by char. But you can read it word by word, this way:
char str[30]; //the word can’t be more than 30 characters long
while(!OpenFile.eof()){
OpenFile >> str;
cout << str;
}
You can also read it line by line, this way:
char line[100]; //a whole line will be stored here
while(!OpenFile.eof()) {
OpenFile.getline(line,100); //where 100 is the size of the array
cout << line << endl;
}
It is recommend that you to use the line-by-line one, or the first technique that was mentioned, i.e., the one which reads char-by-char. The one that reads word-by-word is not good idea, because it won’t read the new line. So if you have new line in the file, it will not display it as a new line, but will append the text to the existing one. But using getline() or get() will show you the file, just as it is.
3.4.2 Checking for Failure with File Commands
Now, we will see how to check if the file opening was successful or not. In fact, there are few good ways to check for that, and we will see a few of them. Notice that where there is X, it can be either “o”, either “i” either nothing (it will then be fstream object).
Example 1: The most usual way
Xfstream File(“cpp-home.txt”);
if (!File) {
cout << “Error opening the file! Aborting…n”;
exit(1);
}
Example 2: If the file is created, return an error
ofstream File("unexisting.txt", ios::nocreate);
if(!File) {
cout << “Error opening the file! Aborting…n”;
exit(1);
}
Example 3: Using the fail() function
ofstream File("filer.txt", ios::nocreate);
if(File.fail()) {
cout << “Error opening the file! Aborting…n”;
exit(1);
}
The new thing in Example 3, is the fail() function. It returns a nonzero value if any I/O error (not end of file) has occurred.
There is an interesting fact that needs to be mentioned here. Say, you have created a file stream, but you haven’t opened a file. This way:
ifstream File; //it could also be ofstream
This way, we have a handle, but we still have not opened the file. If you want to open it later, it can be done with the open() function (which has already been covered in this chapter). But if anywhere in your program, you need to know if currently there is an opened file, you can check it with the function is_open(). It retunrs 0 (false) if a file is not opened, and 1 (true) if there is an opened file. For example:
ofstream File1;
File1.open("file1.txt");
cout << File1.is_open() << endl;
The code above, will return 1, as we open a file (on line 2). But the code below will return 0, because we don’t open a file, but just create a file stream handle:
ofstream File1;
cout << File1.is_open() << endl;
Self Assessment Questions
1. List out the open modes available for opening a file.
2. In what situations can you expect file commands to fail?
3.5 Checking the I/O Status – Flags
The Input/Output system in C++ holds information about the result of every I/O operation. The current status is kept in an object from type io_state, which is an enumerated type (just like open_mode) that has the following values:
godbit
No errors.
eofbit
End of file has been reached
failbit
Non-fatal I/O error
badbit
Fatal I/O error
There are two ways, to receive information about the I/O status. One of them is by calling the function rdstate(). It returns the current status of the error-flags (the above mentioned). For example, the rdstate() function will return goodbit if there were no errors.
The other way to check the I/O status is by using any of the following function:
bool bad();
bool eof(); //Read until the end of the file has been reached
bool fail(); //Check if the file opening was successful
bool good();
The function bad() returns true, if the badbit flag is up. The fail() function returns true if the failbit flag is up. The good() function returns true if there were no errors (the goodbit flag is up). And the eof() function returns true if the end of the file has been reached (the eofbit flag is up).
If an error occurred, you will have to clear it if you want your program to continue properly. To do so, use the clear() function, which takes one parameter. The parameter should be the flag you want to put to be the current status. If you want your program to continue “on clear”, just put ios::goodbit as parameter. But notice that the clear() function can take any flag as parameter. You will see that in the code examples below.
Example 1: Simple status check
//Replace FileStream with the name of the file stream handle
if (FileStream.rdstate() == ios::eofbit)
cout << "End of file!n";
if (FileStream.rdstate() == ios::badbit)
cout << "Fatal I/O error!n";
if (FileStream.rdstate() == ios::failbit)
cout << "Non-fatal I/O error!n";
if (FileStream.rdstate() == ios::goodbit)
cout << "No errors!n";
Example 2: The clear() function
#include <fstream.h>
void main() {
ofstream File1("file2.txt"); //create file2.txt
File1.close();
//this will return error, because ios::noreplace is used
//open_mode, which returns error if the file already exists.
ofstream Test("file2.txt", ios::noreplace);
//The error that the last line returned is ios::failbit
if (Test.rdstate() == ios::failbit)
cout << "Error...!n";
//set the current status to ios::goodbit
Test.clear(ios::goodbit);
//check if it was set correctly
if (Test.rdstate() == ios::goodbit)
cout << "Fine!n";
Test.clear(ios::eofbit); //set it to ios::eofbit. Useless.
//and check again if it is this flag indeed
if (Test.rdstate() == ios::eofbit)
cout << "EOF!n";
Test.close();
}
3.6 Dealing with Binary Files
Although, the files with formatted text (all that were talked about so far) are very useful, sometimes you may need to work with unformatted files, i.e., binary files. They have the same look as your program itself, and it is much different from what comes after using the << and >> operators. The functions that give you the possibility to write/read unformatted files are get() and put(). To read a byte, you can use get() and to write a byte, use put().
get() and put() both take one parameter, a char variable or character. If you want to read/write whole blocks of data, then you can use the read() and write() functions. Their prototypes are:
istream &read(char *buf, streamsize num);
ostream &write(const char *buf, streamsize num);
For the read() function, buf should be an array of chars, where the read block of data will be put. For the write() function, buf is an array of chars, where is the data you want to save in the file. For the both functions, num is a number, that defines the amount of data (in symbols) to be read/written.
If you reach the end of the file, before you have read “num” symbols, you can see how many symbols were read by calling the function gcount(). This function returns the number of read symbols for the last unformatted input operation. And, before going to the code examples, we must take note that if you want to open a file for binary read/write, you should pass ios::binary as an open mode.
Now, let us see some code examples, so that you can see how stuff works.
Example 1: Using get() and put()
#include <fstream.h>
void main() {
fstream File("test_file.txt",ios::out | ios::in | ios::binary);
char ch;
ch='o';
File.put(ch); //put the content of ch to the file
File.seekg(ios::beg); //go to the beginning of the file
File.get(ch); //read one character
cout << ch << endl; //display it
File.close();
}
Example 2: Using read() and write()
#include <fstream.h>
#include <string.h>
void main() {
fstream File("test_file.txt",ios::out | ios::in | ios::binary);
char arr[13];
strcpy(arr,"Hello World!"); //put Hello World! into the array
File.write(arr,5); //put the first 5 symbols into the file- "Hello"
File.seekg(ios::beg); //go to the beginning of the file
static char read_array[10]; //I will put the read data, here
File.read(read_array,3); //read the first 3 symbols- "Hel"
cout << read_array << endl; //display them
File.close();
}
3.7 Some useful functions
· tellg() – Returns an int type, that shows the current position of the inside-pointer. This one works only when you read a file. Example:
#include <fstream.h>
void main() {
//if we have "Hello" in test_file.txt
ifstream File("test_file.txt");
char arr[10];
File.read(arr,10);
//this should return 5, as Hello is 5 characters long
cout << File.tellg() << endl;
File.close();
}
· tellp() – The same as tellg() but used when we write in a file. To summarize: when we read a file, and we want to get the current position of the inside-pointer, we should use tellg(). When we write in a file, and we want to get the current position of the inside-pointer, we should use tellp().
· seekp() – Remember seekg()? We used it when reading a file, and we wanted to go to a specified position. seekp() is the same, but it is used when you write in a file. For example, if we read a file, and we want to get 3 characters back from the current position, we should call FileHandle.seekg(-3). But if we write in a file, and for example, we want to overwrite the last 5 characters, we have to go back 5 characters. Then, we should use FileHandle.seekp(-5).
· ignore() – Used when reading a file. If you want to ignore certain amount of characters, just use this function. In fact, you can use seekg() instead, but the ignore() function has one advantage that you can specify a delimiter rule, where the ignore() function will stop. The prototype is:
istream& ignore(int nCount, delimiter);
where nCount is the amount of characters to be ignored and delimiter is what its name says. It can be EOF if you want to stop at the end of the file. This way, this function is the same as seekg(). But it can also be ‘n’ for example, which will stop on the first new line. Here is an example:
#include <fstream.h>
void main() {
//if we have "Hello World" in test_file.txt
ifstream File("test_file.txt");
static char arr[10];
//stop on the 6th symbol, if you don't meet "l"
//in case you meet "l"- stop there
File.ignore(6,'l');
File.read(arr,10);
cout << arr << endl; //it should display "lo World!"
File.close();
}
· getline() – We have already used this one. This function can be used to read line-by-line, but it can be set to stop reading if it met a certain symbol. Here is how you should pass the parameters to it:
getline(array, array_size, delim);
And here is a code example:
#include <fstream.h>
void main() {
//if we have "Hello World" in test_file.txt
ifstream File("test_file.txt");
static char arr[10];
/*read, until one of these happens:
1) You have read 10
2) You met the letter "o"
3) There is new line
*/
File.getline(arr, 10, 'o');
cout << arr << endl; //it should display "Hell"
File.close();
}
· peek() – This function will return the next character from an input file stream, but it won’t move the inside-pointer. get() for example, returns the next character in the stream, and after that, it moves the inside-pointer, so that the next time you call the get() function, it will return the next character, but not the same one. Well, using peek() will return a character, but it won’t move the cursor. So, if you call the peek() function, two times in succession, it will return the same character.
Consider the following code example:
#include <fstream.h>
void main() {
//if we have "Hello World" in test_file.txt
ifstream File("test_file.txt");
char ch;
File.get(ch);
cout << ch << endl; //should display "H"
cout << char(File.peek()) << endl; //should display "e"
cout << char(File.peek()) << endl; //should display "e" again
File.get(ch);
cout << ch << endl; //should display "e" again
File.close();
}
The peek() function actually returns the ASCII code of the char, but not the char itself. So, if you want to see the character itself, you have to call it the way as shown above.
· _unlink() – Deletes a file. Include io.h in your program, if you are going to use this function. Here is a code example:
#include <fstream.h>
#include <io.h>
void main() {
ofstream File;
File.open("delete_test.txt"); //creates the file
File.close();
_unlink("delete_test.txt"); //deletes the file
//tries to open the file, but if it does not exists
//the function will rreturn error ios::failbit
File.open("delete_test.txt", ios::nocreate);
//see if it returned it
if(File.rdstate() == ios::failbit)
cout << "Error...!n"; //yes, it did
File.close();
}
· putback() – This function will return the last read character, and will move the inside-pointer, one with –1 char. In other words, if you use get() to read a char, then use putback(), it will show you the same character, but it will set the inside-pointer with –1 char, so the next time you call get() again, it will again show you the same character. Here is a code example:
#include <fstream.h>
void main() {
//test_file.txt should have this text- "Hello World"
ifstream File("test_file.txt");
char ch;
File.get(ch);
cout << ch << endl; //it will display "H"
File.putback(ch);
cout << ch << endl; //it will again display "H"
File.get(ch);
cout << ch << endl; //it will display "H" again
File.close();
}
· flush() – When dealing with the output file stream, the date you save in the file, is not actually immediately saved in it. There is a buffer, where it is kept, and when the buffer gets filled, then the data is put in the real file (on your disk). Then the buffer is emptied, and so on. But if you want to save the data from the buffer, even if the buffer is still not full, use the flush() function. Just call it this way- FileHandle.flush(). And the data from the buffer will be put in the physical file, and the buffer will be emptied.
Self Assessment Questions
1. What is the difference between peek() and get()?
2. How are peek() and putback() related?
3.8 Manipulators
A manipulator is an identifier that can be inserted into an output stream or extracted from an input stream in order to produce a desired effect. For example, endl is a commonly-used manipulator which inserts a newline into an output stream and flushes it. Therefore,
cout << 10 << endl;
has the same effect as:
cout << 10 << 'n';
In general, most formatting operations are more easily expressed using manipulators than using setf. For example,
cout << oct << 10 << endl;
is an easier way of saying:
cout.setf(ios::oct, ios::basefield);
cout << 10 << endl;
Some manipulators also take parameters. For example, the setw manipulator is used to set the field width of the next IO object:
cout << setw(8) << 10; // sets the width of 10 to 8 characters
The manipulators available have already been summarized in Section 2.2.3. Let us now look at some of the examples.
Example 1: A simple output manipulator
#include <iostream>
#include <iomanip>
using namespace std;
ostream &sethex(ostream &stream) {
stream.setf(ios::showbase);
     stream.setf(ios::hex, ios::basefield);
return stream;
}
int main() {
  cout << 256 << " " << sethex << 256;
     return 0;
}
Output:
256 0x100"
Example 2: Creating an output manipulator
#include <iostream>
#include <iomanip>
using namespace std;
ostream &mySetting(ostream &stream) {
     stream.setf(ios::left);
     stream << setw(10) << setfill('$');
   return stream;
}
int main() {
cout << 10 << " " << mySetting << 10;
  return 0;
}
Output:
10 10$$$$$$$$
Example 3: EndLine manipulator (using escape sequence n and member function flush)
#include <iostream>
using std::ostream;
using std::cout;
using std::flush;
ostream& endLine( ostream& output ) {
    return output << 'n' << flush; // issue endl-like end of line
}
int main()
{

   cout << "-----" << endLine;
return 0;
}
Example 4: Create user-defined, non-parameterized stream manipulators: bell manipulator (using escape sequence a)
#include <iostream>
using std::ostream;
using std::cout;
using std::flush;
ostream& bell( ostream& output )
{

return output << 'a';  // issue system beep
}
int main()
{

cout << bell; // use bell manipulator
return 0;
}
Example 5: Create user-defined, non-parameterized stream manipulators: carriageReturn manipulator (using escape sequence r)
#include <iostream>
using std::ostream;
using std::cout;
using std::flush;
ostream& carriageReturn( ostream& output )
{

return output << 'r';  // issue carriage return
}
int main()
{

cout << carriageReturn << "-----" ;
return 0;
}
Example 6: tab manipulator (using escape sequence t)
#include <iostream>
using std::ostream;
using std::cout;
using std::flush;
ostream& tab( ostream& output )
{

return output << 't';  // issue tab
}
int main() {
cout << tab << 'b' << tab << 'c' ;
return 0;
}
Output:
b                  c
Example 7: Using Manipulators
// Sample use of the following manipulators:
// resetiosflags
// setiosflags
// setbase
// setfill
// setprecision
// setw
#include <iostream>
#include <iomanip>
using namespace std;
const double d1 = 1.23456789;
const double d2 = 12.3456789;
const double d3 = 123.456789;
const double d4 = 1234.56789;
const double d5 = 12345.6789;
const long l1 = 16;
const long l2 = 256;
const long l3 = 1024;
const long l4 = 4096;
const long l5 = 65536;
int base = 10;
void DisplayDefault() {
cout << endl << "default display" << endl;
cout << "d1 = " << d1 << endl;
cout << "d2 = " << d2 << endl;
cout << "d3 = " << d3 << endl;
cout << "d4 = " << d4 << endl;
cout << "d5 = " << d5 << endl;
}
void DisplayWidth( int n ) {
cout << endl << "fixed width display set to " << n << ".n";
cout << "d1 = " << setw(n) << d1 << endl;
cout << "d2 = " << setw(n) << d2 << endl;
cout << "d3 = " << setw(n) << d3 << endl;
cout << "d4 = " << setw(n) << d4 << endl;
cout << "d5 = " << setw(n) << d5 << endl;
}
void DisplayLongs() {
cout << setbase(10);
cout << endl << "setbase(" << base << ")" << endl;
cout << setbase(base);
cout << "l1 = " << l1 << endl;
cout << "l2 = " << l2 << endl;
cout << "l3 = " << l3 << endl;
cout << "l4 = " << l4 << endl;
cout << "l5 = " << l5 << endl;
}
int main( int argc, char* argv[] ) {
DisplayDefault( );
cout << endl << "setprecision(" << 3 << ")" << setprecision(3);
DisplayDefault( );
cout << endl << "setprecision(" << 12 << ")" << setprecision(12);
DisplayDefault( );
cout << setiosflags(ios_base::scientific);
cout << endl << "setiosflags(" << ios_base::scientific << ")";
DisplayDefault( );
cout << resetiosflags(ios_base::scientific);
cout << endl << "resetiosflags(" << ios_base::scientific << ")";
DisplayDefault( );
cout << endl << "setfill('" << 'S' << "')" << setfill('S');
DisplayWidth(15);
DisplayDefault( );
cout << endl << "setfill('" << ' ' << "')" << setfill(' ');
DisplayWidth(15);
DisplayDefault( );
cout << endl << "setprecision(" << 8 << ")" << setprecision(8);
DisplayWidth(10);
DisplayDefault( );
base = 16;
DisplayLongs( );
base = 8;
DisplayLongs( );
base = 10;
DisplayLongs( );
return 0;
}
Output:
default display
d1 = 1.23457
d2 = 12.3457
d3 = 123.457
d4 = 1234.57
d5 = 12345.7
setprecision(3)
default display
d1 = 1.23
d2 = 12.3
d3 = 123
d4 = 1.23e+003
d5 = 1.23e+004
setprecision(12)
default display
d1 = 1.23456789
d2 = 12.3456789
d3 = 123.456789
d4 = 1234.56789
d5 = 12345.6789
setiosflags(4096)
default display
d1 = 1.234567890000e+000
d2 = 1.234567890000e+001
d3 = 1.234567890000e+002
d4 = 1.234567890000e+003
d5 = 1.234567890000e+004
resetiosflags(4096)
default display
d1 = 1.23456789
d2 = 12.3456789
d3 = 123.456789
d4 = 1234.56789
d5 = 12345.6789
setfill('S')
fixed width display set to 15.
d1 = SSSSS1.23456789
d2 = SSSSS12.3456789
d3 = SSSSS123.456789
d4 = SSSSS1234.56789
d5 = SSSSS12345.6789
default display
d1 = 1.23456789
d2 = 12.3456789
d3 = 123.456789
d4 = 1234.56789
d5 = 12345.6789
setfill(' ')
fixed width display set to 15.
d1 = 1.23456789
d2 = 12.3456789
d3 = 123.456789
d4 = 1234.56789
d5 = 12345.6789
default display
d1 = 1.23456789
d2 = 12.3456789
d3 = 123.456789
d4 = 1234.56789
d5 = 12345.6789
setprecision(8)
fixed width display set to 10.
d1 = 1.2345679
d2 = 12.345679
d3 = 123.45679
d4 = 1234.5679
d5 = 12345.679
default display
d1 = 1.2345679
d2 = 12.345679
d3 = 123.45679
d4 = 1234.5679
d5 = 12345.679
setbase(16)
l1 = 10
l2 = 100
l3 = 400
l4 = 1000
l5 = 10000
setbase(8)
l1 = 20
l2 = 400
l3 = 2000
l4 = 10000
l5 = 200000
setbase(10)
l1 = 16
l2 = 256
l3 = 1024
l4 = 4096
l5 = 65536
3.9 Summary
We are well acquainted with programs that use input only from the keyboard, and output only to the screen. If we were restricted to use only the keyboard and screen as input and output devices, it would be difficult to handle large amounts of input data, and output data would always be lost as soon as we turned the computer off. To avoid these problems, we can store data in some secondary storage device, usually magnetic tapes or discs. Data can be created by one program, stored on these devices, and then accessed or modified by other programs when necessary. To achieve this, the data is packaged up on the storage devices as data structures called files.
Before we can work with files in C++, we need to become acquainted with the notion of a stream. We can think of a stream as a channel or conduit on which data is passed from senders to receivers. As far as the programs we will use are concerned, streams allow travel in only one direction. Data can be sent out from the program on an output stream, or received into the program on an input stream. For example, at the start of a program, the standard input stream "cin" is connected to the keyboard and the standard output stream "cout" is connected to the screen. In fact, input and output streams such as "cin" and "cout" are examples of (stream) objects. The essential characteristic of stream processing is that data elements must be sent to or received from a stream one at a time, i.e. in serial fashion.
In this chapter we have seen the entire iostream hierarchy, which has been explained at the very beginning of this chapter. But, C++ file input and output are typically achieved by using an object of one of the following classes: ifstream for reading input only; ofstream for writing output only; fstream for reading and writing from/to one file. All three classes are defined in <fstream.h>.
This chapter has discussed comprehensively the C++ streams. Various examples provided throughout this chapter illustrate the use of these streams, and there is no doubt that working with streams is a relatively easy task.
3.10 Terminal Questions
1. What are C++ streams?
2. Describe the iostream hierarchy.
3. There are two positions associated with every open file. Explain these positions.
4. How do you connect and disconnect streams?
5. What are the different ways to open a file?
6. What is an open mode?
7. Mention the different ways to receive information of an I/O status.
8. How is dealing with normal files different from dealing with binary files?
9. List the usage of the various output manipulators available in C++.
10. Implement the examples provided in this chapter and record the results

No comments:

Post a Comment