File Processing

Files and Streams
The source of input may be keyboard, files on hard disk or other devices. The output destination may be screen, files on hard disk or other devices.
For standard IO devices keyboard and screen, C++ provides iostream objects cin, cout, cerr, clog. For other devices, you have to create objects yourself.
An ifstream object can be used to input from a file on disk, an ofstream object can be used to output to a file on disk. A fstream object can be used both to input from and output to a file. You can use their methods open and close to open and close a certain file. For example, you can use.
fstream file("Anuj.txt", iso::out | ios::in);
The file connected to an object of ifstream, ofstream or fstream class will be automatically closed when the object leave scope and is destroyed. However, it is a good practice to explicitly close the file as soon as you do not use it any longer. Reasons are:
Reduce resource usage;
Improve the program’s clarity;
Prevent future misuse.
File Processing
In C++, file handling means doing things with files, such as opening/closing,reading/writing, deleting/copying/moving/renaming etc.
File processing in C++ is performed using the fstream class. Unlike the FILE structure, fstream is a complete C++ class with constructors, a destructor and overloaded operators.
To perform file processing, you can declare an instance of an fstream object. If you do not yet know the name of the file you want to process, you can use the default constructor.
Unlike the FILE structure, the fstream class provides two distinct classes for file processing. One is used to write to a file and the other is used to read from a file.
File Open Modes
Initializing a File
When processing a file, you will typically specify the type of operation you want to perform. The operation is specified using what is referred to as a file mode. It can be one of the following:
Mode Description
ios::app If FileName is a new file, data is written to it.
If FileName already exists and contains data, then it is opened, the compiler goes to the end of the file and adds the new data to it.
ios::ate If FileName is a new file, data is written to it and subsequently added to the end of the file.
If FileName  already exists and contains data, then it is opened and data is written in the current position.
ios::in If FileName is a new file, then it gets created fine as an empty file.
If FileName already exists, then it is opened and its content is made available for processing
ios::out If FileName is a new file, then it gets created fine as an empty file. Once/Since it gets created empty, you can write data to it.
If FileName already exists, then it is opened, its content is destroyed, and the file becomes as new. Therefore you can create new data to write to it. Then, if you save the file, which is the main purpose of this mode, the new content is saved it.*This operation is typically used when you want to save a file
ios::trunc If FileName already exists, its content is destroyed and the file becomes as new
ios::nocreate If FileName is a new file, the operation fails because it cannot create a new file.
If FileName already exists, then it is opened and its content is made available for processing
ios::noreplace If FileName is a new file, then it gets created fine.
If FileName already exists and you try to open it, this operation would fail because it cannot create a file of the same name in the same location.
Saving a FileSaving a File
One of the operations you can perform on a file consists of saving it, which is equivalent to storing its value(s) to a medium. To save a file, you can first declare an instance of the ofstream class using one of its constructors from the following syntaxes:
ofstream();
ofstream(const char* FileName, int FileMode);
The default constructor allows you to initiate file processing without giving details. If you decide to use the default constructor, you can then call one of the methods we will see to perform the necessary operation.
The ofstream(const char* FileName, int FileMode) constructor provides a complete mechanism for creating a file. It does this with the help of its two arguments. The first argument, FileName, is a string that specifies the name of the file that needs to be saved. The second argument, FileMode, specifies the kind of operation you want to perform on the file. It can be one of the modes we listed above.
Once you have decided what you want to do with a file, you use the << operator to save each value. Here is an example:
#include
#include < iostream >
using namespace std;
int main()
{
char FirstName[30], LastName[30];
int Age;
char FileName[20];
cout << "Enter First Name: ";
cin >> FirstName;
cout << "Enter Last Name: ";
cin >> LastName;
cout << "Enter Age: ";
cin >> Age;

cout << "\nEnter the name of the file you want to create: ";
cin >> FileName;
ofstream Students(FileName, ios::out);
Students << FirstName << "\n" << LastName << "\n" << Age;
cout << "\n\n";
return 0;
}
If you had used the default constructor to declare an ofstream variable, you can call the open() method to actually process the file. The syntax of the open method is:
void open(const char* FileName, int Mode, int nProt = filebuf::openprot );
This method behaves exactly like, and uses the same arguments as, the constructor we described above. The first argument represents the name of the file you are dealing with and the FileMode argument follows the modes of the above table.
Because the fstream class in this case is declared as ofstream, the compiler is aware that you want to save a file (in reality, the use of ofstream means that you want to write to a file, in other words you want the FileMode with a value of ios::out), you can use the second constructor with just the FileName as argument or you can call the open() method with only the name of the file.
After using a file, you should close it. This is taken care by using the ofstream::close() method whose syntax is:
void close();
Opening a File
Besides saving, another operation you can perform consists of opening an already existing file to have access to its contents. To do this, C++ provides the ifstream class. Like ofstream, the ifstream class provides various constructors you can use, two of which are particularly important. If you have enough information about the file you want to open, you can use the following constructor:
ifstream(const char* FileName, int FileMode);
The first argument of the constructor, FileName, is a constant string that represents the file that you want to open. The FileMode argument is a natural number that follows the table of modes as we described above.
If necessary, you can also declare an empty instance of the ifstream class using the default constructor:
ifstream();
After declaring this constructor, you can use the ifstream::open() method to formally open the intended file. The syntax of the open() method is:
open(const char* FileName, int FileMode);
This method uses the same arguments as the above constructor. By default, when declaring an instance of the ifstream class, it is assumed that you want to open a file; that is, you want to use the FileMode attribute with a value of ios::in. Therefore, the second argument is already set to ios::in value. This allows you to call the open() method with just the FileName value.
After using the ifstream class, you can close it using the ifstream::close() method. Here is an example:
#include < fstream >
#include < iostream >
using namespace std;
int main()
{
char FirstName[30], LastName[30];
int Age;
char FileName[20];
/* cout << "Enter First Name: ";
cin >> FirstName;
cout << "Enter Last Name: ";
cin >> LastName;
cout << "Enter Age: ";
cin >> Age;
cout << "\nEnter the name of the file you want to create: ";
cin >> FileName;
ofstream Students(FileName, ios::out);
Students << FirstName << "\n" << LastName << "\n" << Age;
*/
cout << "Enter the name of the file you want to open: ";
cin >> FileName;
ifstream Students(FileName);
Students >> FirstName >> LastName >> Age;
cout << "\nFirst Name: " << FirstName;
cout << "\nLast Name: " << LastName;
cout << "\nEnter Age: " << Age;
cout << "\n\n";
return 0;
}
Writing to a File:
While doing C++ programming, you write information to a file from your program using the stream insertion operator (<<) just as you use that operator to output information to the screen. The only difference is that you use an ofstream or fstream object instead of the cout object.
Reading from a File:
You read information from a file into your program using the stream extraction operator (>>) just as you use that operator to input information from the keyboard. The only difference is that you use an ifstream or fstream object instead of the cin object.
Example: -
#include < fstream >
#include < iostream >
using namespace std;
int main ()
{
char data[100];
// open a file in write mode.
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// write inputted data into the file.
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// again write inputted data into the file.
outfile << data << endl;
// close the opened file.
outfile.close();
// open a file in read mode.
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// write the data at the screen.
cout << data << endl;
// again read the data from the file and display it.
infile >> data;
cout << data << endl;
// close the opened file.
infile.close();
return 0;
}
Output: -
$./a.out
Writing to the file
Enter your name: Anuj
Enter your age: 38
Reading from the file
Anuj
38
File Position Pointers:-
Both istream and ostream provide member functions for repositioning the file-position pointer. These member functions are seekg ("seek get") for istream and seekp ("seek put") for ostream.
The argument to seekg and seekp normally is a long integer. A second argument can be specified to indicate the seek direction. The seek direction can be ios::beg (the default) for positioning relative to the beginning of a stream, ios::cur for positioning relative to the current position in a stream or ios::end for positioning relative to the end of a stream.
ios::beg //default -- count from beginning of the file
ios::cur //count from current location
ios::end //count from the end of the file
fstream file( Frank.txt , ios::in | ios::out);
Sequential Access File
In computer’s internal memory, different integers such as 7 or 7777 are stored with the same number of bytes. But in a file they are stored in different sizes. Therefore, when you overwrite an original number with a longer one in a file, it will overwrite the following field.
For this reason, this kind of files which store data in varied length are called “sequential access files”. You have no idea on exactly how long each record occupies. Therefore, to find a particular record in the file, you have to read from the first one sequentially until you reach the one you want. You can not jump to a certain record directly. If you want to modify a record in the middle of the file, unless the length of the record is not changed, you have to first copy the records before the one to be updated into a file, then append the updated record to that file, then append the records following the updated one to the file.
Random Access File and Object Serialization
Random access file is opened in the same way as normal files, but the way to write data in it is different. Complete objects instead of primitives are read or written with class iostream s method read and write. The object to be read and written can contain member objects, pointers and references to other objects, and other objects can again contain member objects, pointers and references. Method write will write everything necessary into the file, including the type information and the whole network of objects, so that later method read can recover it. The process of breaking an object into data stream is called object serialization .
There is one restraint: the object to be serialized and all its network objects should all have fixed size. If the class contains a char * data member, because the length of the string is variable, method write can not properly allocate space for each record, and run-time error may happen. In such a case, use char [ ] instead of char *.
Method read and write have the same functionality as Java s readObject and writeObject method of class ObjectInputStream and ObjectOutputStream.
Because you can not decide the format or sequence of each field, this way of IO is also called unformatted IO, whereas conventional way of IO is called formatted IO.
Method write takes two arguments. First argument is the address of the object to be serialized, and it should be casted to the type of “const char *” type. The second argument is an integer of size_t specifying the number of bytes of the record. Keyword sizeof can be used to get this size. Method read has similar arguments as write, except that the pointer type is char * .
file.write(reinterpret_cast(&recordName), sizeof(recordName));
file.read(reinterpret_cast(&recordName), sizeof(recordName));
Example: -
#include "iostream.h"
//class Base
class Base
{
public:
Base(const int = 0);
Base(const Base &);
const int get() const;
protected:
const Base & operator=(const Base &); private:
int member;
};
Base::Base(const int i): member(i) {}
Base::Base(const Base & b): member(b.member) {}
const Base & Base::operator=(const Base & rv)
{
member = rv.member;
return *this;
}
const int Base::get() const
{ return member; }
//class Derived
class Derived : public Base
{
public:
Derived(const int = 0, const int = 0);
Derived(const Derived &);
const Derived & operator=(const Derived &);
void print() const;
private:
int member;
};
Derived::Derived(const int i1, const int i2) : Base(i1), member(i2)
{}
Derived::Derived(const Derived & d) : Base(d), member(d.member)
{}
const Derived & Derived::operator=(const Derived & rv)
{
member = rv.member;
Base::operator=(rv);
return *this;
}
void Derived::print() const
{ cout << "Base member is " << Base::get() << endl;
cout << "Derived member is " << member << endl;
}
//class ClientData
class ClientData
{
public:
ClientData(int = 0, Derived * = 0);
int getId() const;
void print() const;
private:
int id;
Derived * derived;
};
ClientData::ClientData(int i, Derived * d): id(i), derived(d) {}
int ClientData::getId() const
{
return id;
}
void ClientData::print() const
{
cout << id << ": ";
derived->print();
}
//main
int main(int argc, char* argv[])
{
Derived d1(111, 222);
ClientData c1(37, &d1);
cout << "Client c1 = \n";
c1.print();
cout << "\n";
fstream file("customerfile.txt", ios::out | ios::in);
int size = sizeof(ClientData);
file.seekp(c1.getId() * size);
file.write(reinterpret_cast(&c1), size);
file.close();
file.open("customerfile.txt", ios::out | ios::in);
ClientData empty; // create an empty object file.seekg(37 * size);
file.read(reinterpret_cast(O), size);
cout << "Empth is now:\n";
empty.print();
file.close();
return 0;
}
Output will be:
Client c1 =
37: Base member is 111
Derived member is 222
Empth is now:
37: Base member is 111
Derived member is 222
In this example, data member “id” of class ClientData acts as the primary key.
File and Directory Manipulation
Win32 API provides a set of functions to manipulate files and directories. They are listed in MSDN under title “About File I/O”. Following is a very useful piece of code which go through all sub-directories of the current directory, and change replace a certain string with another in all files that conform to a certain file name filter:
#define SUBSTR1 "ulStatus = RET_EXCEPTION" // length = 24, not counting NULL
#define SUBSTR2 "ulStatus = RET_EXCEPTION" // length = 25, not counting NULL
#define SUBSTR3 "ulStatus = RET_EXCEPTION" // length = 25, not counting NULL


Free Web Hosting