File Handling
File handling is the method of saving your data into secondary storage device. Remember that all data in the program will be in RAM and will be lost once the program is exited. If you need any data to be permanently stored you need to store them in files in disks.
In C++, file handling is achieved with classes derived from fstream (file stream).
There are three library classes for file handling which are fstream, ifstream and ofstream.
ifstream is exclusively used for input operations, ofstream for output operations. fstream class can be used for both.
You create an object of any of these three classes and use insertion operator (<<) to write to a file and extraction operator (>>) to read from a file. There is no need to explicitly close the file. Destructor takes care of closing the file.
Opening a file
A file can be opened using constructor of any of ifstream, ofstream or fstream. The file name may be given as a parameter to constructor.
You need to include fstream header file to use any file operation.
Here we are trying to open a file called a.txt for reading - as we use ifstream object for that. And we open a file called b.txt for writing.
If file opening fails, the fstream object becomes false. This can be used for verifying whether file open was successful or not. Other methods of checking file open are file.fail() or file.isopen()
A file object can also be defined first and then opened later using open() method.
ifstream infile;
infile.open("d:/programs/abcd");
You can use flags to mention mode of opening the file. The default mode is ios::in for ifstream objects and ios::out for ofstream objects.
File opening modes are
-
ios::in - read
-
ios::out - write
-
ios::app - append
-
ios::ate - file pointer at the end
-
ios::trunc - truncate existing file
-
ios::binary - binary file
These modes can be bitwise OR-ed also. e.g. ios::in|ios::binary.
Open file for both read and write
fstream objects are used to open a file for both reading and writing. You must use ios::in|ios::out mode specifier to specify both read and write operations.
fstream file3;
file3.open("../abcd",
ios::in|ios::out);
The above code opens a file abcd in the parent directory of cwd for reading and writing.
Be careful about file names. Directory separator should be forward slash(/) - even in Windows based systems.
Reading and writing to a file:
Extraction operator(>>) is used for reading from a file. Insertion operator(<<) is used for writing to a file. Both of these skip white spaces. (similar to scanf in C)
Code above writes cubes of numbers from 1 to 10 to the file a.txt. We have not mentioned mode. But that is okay because default mode is write for ofstream objects.
And also we verified if file open is successful by checking whether file1 is true or false. Any invalid operation on file sets the file object to be a boolean false value.
Next let us write a program to read filename from user and then display its contents on the screen.
Here we try to open the file given by user. If we are unable to open the file, there is an error message. If file open is successful, the contents of the file are displayed on the screen.
Where is eof?
We need not use eof() function because if there is end of file and file read is unsuccessful, then extraction operator returns a false.
But if we really want to, we can still use eof() method of fstream class. But we need to be little bit careful.
while(!file.eof())
{
file>>word;
cout<<word;
}
This will print the last word twice. Unless you modify the cout as
if(file.eof())
break;
else
cout<<word;
What will be the output of the above program?
./a.out
Filename:readfile.cpp
#include<iostream> #include<fstream> using namespace std; int main() { char filename[30]; cout<<"Filename:"; cin>>filename; ifstream file(filename); if(!file) { cout<<"File open error"; return 1; } string word; while(file>>word) cout<<word<<" "; return 0; }
What is happening? Why is this code so ugly? Where are all the line breaks?
Line breaks are ignored like all other white spaces - spaces, tabs, newlines. When we read using extraction operator, the string will be read until there is a white space. Instead of reading one line, one word is read at a time.
In fact the same thing happens when you read the string using cin object too. The reading will stop when there is white space.
Then how do you read one line at a time?
getline() function
getline() function of fstream class can be used to read string till newline character - i.e. read one line at a time.
Syntax
fstream.getline(char *str, int maxlen)
Let us rewrite the previous program so that we read one line at a time from the file and display it on screen.
Similar to PODs, user defined class objects can be read/written using insertion operator and extraction operator if we have overloaded >> and << operators for these classes (using friend functions).
Reading and writing to a binary file
A binary file can be opened by adding the flag ios::binary in open statement or in overloaded constructor call.
e.g.
ifstream file1("abce",ios::binary);
fstream file2;
file2.open("abcd",ios::binary|ios::out);
To read and write to binary file, you need to use read() and write() functions.
These two functions require first parameter as a char pointer which points to the variable and second parameter as size of the variable to be read/written.
We are writing numbers from 0 to 10 into a binary file - a.1
Next let us see how to read a binary file.
Note that both read() and write() require a character pointer as first parameter. And if read operation fails because eof is reached, then it returns a false.
Moving file pointers
There are four functions used for moving file pointers.
-
seekg(num_bytes[,from])- moves input position indicator
-
tellg() - returns input position indicator
-
seekp(num_bytes[,from]) - moves output position indicator
-
tellp() - returns output position indicator
These four functions use optional flags ios::beg, ios::end, ios::cur to specify from where. (g is for getpointer and p is for put pointer)
Note that both read() and write() require a character pointer as first parameter. And if read operation fails because eof is reached, then it returns a false.
If the file a.txt contains
hello world
and we type filename as a.txt, then the output will be
lo
The reason is, seekg(3,ios::beg) will move the get pointer (reading file pointer 3rd character. And >> reads only one word. So the output is lo.
Let us add a tellg to that code.
Output is
lo
tellg gives 5
tellg() gives location of get pointer. After reading hello and space, file pointer is at 5th position.
Let us use seekp() now.
Here we are opening a file for writing and first write Hello world onto it. Then we move the file pointer to 0th position and write Goodbye.
So file will contain
Goodbyeorld
If you are familiar with C language, seekg and seekp are similar to fseek() in C and tellg() and tellp() are similar to ftell() in C.
Reading and writing objects to files
We can write objects to files either as a binary data or text data.
Writing objects to binary file is quite simple. As we have discussed earlier, we need to use file.write() and file.read() methods and addresses of objects type cast into character pointers.
We are writing two objects of the class MyInteger to a binary file using write() method and then reading them back using read() method.
Remember that we have to typecast addresses of these objects to char*.
Second method of writing objects to files - text files is to overload << operator for that class with first parameter being ofstream object or fstream object.
Similarly to read objects from text files, we can overload extraction operator (>>) for that class.
Let us overload these operators for MyInteger class. Please remember these two have to be friend functions.
In this quite lengthy program, we are overloading >> and << operators for MyInteger class. And writing objects of this class to file and reading them from file.
If you want not to violate data hiding of the class and not to use friends, then you can add setNum() function to the class and the >> operator and << operator need not be friends of the class.