Jimma University
School of Computing and
Informatics.
Fundamental of Programming II
Chapter six: Working with files
Jimma, Ethiopia.
Introduction to files
Many real life problems handle large volumes of data and in such situations, we
need to use some devices such as hard disk and flash disk to store the data.
Data is stored in devices using the concept of files
File is an area in secondary storage used to hold information.
Programs can be designed to perform the read and write operations on these
devices.
A program typically involves either or both of the following kinds of data
communication:
Data transfer between the console unit and the program.
Data transfer between the program and a disk file.
Cont…
The standard I/O header file, iostream, contains data types and variables that are
used only for input from the standard input device and output to the standard
output device.
In addition the I/O system of C++ handles file operations which are very much
similar to the I/O operations.
It uses file streams as an interface between the program and files.
The stream that supplies data to the program is known as input stream and the
one that receives data from the program is known as output stream.
i.e the input stream extracts data from file and output stream inserts/writes
data to the file.
File Program ( Input stream) - reads
Program File (Output stream) – write
I/O operations
The input operations involves the creation of an input stream and linking
it to the program and the input file.
The output operations involves the establishing an output stream with the
necessary links with the program and the output file.
Streams and Files
cont’d
Page-5
5
Class for file stream operations
The I/O system of C++ contains a set of classes that define the file handling
methods (includes ifstream, ofstream and fstream).
These classes are derived from fstreambase and from the corresponding
iostream class
These classes are designed to manage disk files, are declared in fstream and
therefore we must include this file in any program that uses files.
Data Type Description
ofstream This data type represents the output file stream and is used to
create files and to write information to files.
ifstream This data type represents the input file stream and is used to read
information from files.
fstream This data type represents the file stream generally, and has the
capabilities of both ofstream and ifstream which means it can
create files, write information to files, and read information from
files.
Opening and Closing a File
If we want to use a disk file, we need to decide the following things about the file
and its intended use:
Suitable name for the file(can’t use /|\”:?><*)
Data types and structure
Purpose Opening method
A file must be opened before you can read from it or write to it.
A file stream can be defined using a classes ifstream, ofstream and fstream that
are contained in the header file fstream.
The class to be used depends upon the purpose(i.e whether you want to read
from a file or write data onto it).
A file can be opened in two ways:
Using the constructor function of the class- useful when only one file in a
stream is used
Using a member function called open() of the class – to manage multiple files
using one stream
Opening files Using the constructor
Constructor is used to initialize objects while they are created. File stream is
used to initialize the file stream object.
It involves the following steps:
Create a file stream object to manage the stream using the appropriate stream
class
Initialize the file object with the desired filename
Syntax to open a file using constructor:
<File stream class > <stream Object>(<file name>);
E.g: oftream outfile(“results”); This creates outfile as an ofstream object that
manages the output stream. The statement also opens the file results and attaches
it to the output stream outfile.
Similarly, the following statement declares infile as an ifstream object and
attaches it to the file data for reading(input).
ifstream infile(“data”);
We can also use the same file for both writing and reading data.
Example 1: Open a file for writing only
#include<iostream> The file My file1.txt is opened
in write only mode.
#include<fstream>
using namespace std;
int main(){
//Name of the file to be
created //and opened.
char fname[] = "My file1.txt";
//Open the file using the
//constructor
ofstream outfile(fname);
cout<<"\nThe file "<<fname<<" is
opened in write only mode.";
outfile.close();//Close the file
return 0;
}
Example 2: Open a file for reading only
#include<iostream> The file My file1.txt is opened in
read only mode.
#include<fstream>
using namespace std;
int main(){
//Name of the file to be
//created and opened.
char fname[] = "My
file1.txt";
//Open the file using the
//constructor
ifstream infile(fname);
cout<<"\nThe file "<<fname<<"
is opened in read only
mode.";
infile.close();//Close the
file
return 0;
}
Cont…
A connection to a file is closed automatically when the stream object
expires.
When a file is opened for writing only, a new file is created if there is no
file of that name.
If a file by that name is already exists, then its contents are deleted and
the file is presented as a clean file.
Opening files using member function
The function open() can be used to open multiple files using the same stream
object.
For example we may want to process set files sequentially and in such cases, we
may create a single stream object and use it to open each file in turn.
In order to open a file with a stream object we use its member function open:
<Stream_object> .open (filename);
Where filename is a string representing the name of the file to be opened, and
mode is an optional parameter with a combination of the file mode flags.
Cont…
Example:
ofstream outfile;
outfile.open(“file1);
---
outfile.close();
outfile.open(“file2”);
---
outfile.close();
outfile.open(“file3”);
---
outfile.close();
Note: A stream object can be connected to only one file at a time
Reading and Writing text files
We can use our file streams the same way we are already used to use cin
and cout, with the only difference that we have to associate these streams
with physical files.
Simply use the << and >> operators in the same way you do when
performing console I/O except that instead of using cin and cout, you
substitute a stream that is linked to a file.
Example 3: Writing to files
#include<iostream>
#include<fstream>
using namespace std;
int main(){
ofstream out ("inventory");
out <<“Banana unit price: " << 60 << endl;
out << “Mango Unit Price: " << 100<< endl;
out.close ( );
return 0;
}
Example 3: Reading from files
Banana unit price: 60
#include<iostream> Mango Unit Price: 100
#include<fstream>
using namespace std;
int main(){
char data[50];
ifstream in;
in.open("inventory");
in>>data;//getline(in,data);
cout<<data;
in>>data;//getline(in,data);
cout<<"\n"<<data;
in.close ( );
return 0;
}
Detecting end of file
Detection of an end of file is necessary for preventing further attempt to
read data from file.
For e.g:
while(infile)
an ifstream, infile, returns a value zero if any error occurs in the file
operations including end of file condition.
An other approch to detect end of file condition is to use the eof().
eof() is member function of ios class. And it returns a non zero value
if the end of file condition is encountered, and a zero, otherwise.
Checking if file stream is opened?
To check if a file stream was successful opening a file, you can do it by
calling to member is_open.
This member function returns a bool value of true in the case that
indeed the stream object is associated with an open file, or false
otherwise:
if (myfile.is_open()) {
/* ok, proceed with output */
}
Example: to check if is opened
#include<iostream>
#include<fstream>
using namespace std;
int main(){
char fname[] = "My file1.txt";
ofstream myfile;
myfile.open(fname);
if (myfile.is_open()){
cout<<"\nThe file "<<fname<<" is opened in write only.";
myfile.close();//Close the file
}
else{
cout<<"Unable to open "<<fname;
}
return 0;
}
More about open(): File modes
Following is the standard syntax for open() function, which is a member of
fstream, ifstream, and ofstream objects.
void open(const char *filename, ios::openmode mode);
Note: the first argument specifies the name and location of the file to be
opened and the second argument of the open() member function defines the
mode in which the file should be opened.
Mode Flag Description
ios::app Append mode. All output to that file to be appended to the end.
ios::ate Open a file for output and move the read/write control to the end of the
file.
ios::in Open a file for reading.
ios::out Open a file for writing.
ios::trunk If the file already exists, its contents will be deleted before opening the
file.
ios::binary Cause the file to be opened in binary mode.
ios::nocreate If the file does not exist, the open operation fails.
ios::noreplace If the file exists, the open operation fails.
Cont…
All these flags can be combined using the bitwise operator OR (|).
For example if you want to open a file in write mode and want to truncate it in
case it already exists, following will be the syntax:
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunk );
Similar way, you can open a file for reading and writing purpose as follows:
fstream afile;
afile.open("file.dat", ios::out | ios::in );
Default file open modes
Each of the open member functions of classes ofstream, ifstream and fstream
has a default mode that is used if the file is opened without a second argument.
For ifstream and ofstream classes, ios::in and ios::out are automatically and
respectively assumed, even if a mode that does not include them is passed as
second argument to the open member function (the flags are combined).
For fstream, the default value is only applied if the function is called without
specifying any value for the mode parameter. If the function is called with any
value in that parameter the default mode is overridden, not combined.
Closing a File
After finished with input and output operations on a file we have to closed it so that the
operating system is notified and its resources become available again.
The stream member function close takes flushes the associated buffers and closes the
file. Following is the standard syntax for close() function, which is a member of
fstream, ifstream, and ofstream objects.
void close();
Syntax to use close(); myfile.close();
Once this member function is called, the stream object can be re-used to open another
file, and the file is available again to be opened by other processes.
In case that an object is destroyed while still associated with an open file, the
destructor automatically calls the member function close.
Note:
When a C++ program terminates, it automatically closes flushes all the streams,
release all the allocated memory and close all the opened files.
it is always a good practice that a programmer should close all the opened files before
program termination.
File pointers and their manipulators
Each file has associated pointers known as file pointers.
One of them is called input pointer( or get pointer ) and the other one is called
output pointer (or put pointer).
We can use these pointers to move through the files while reading and writing.
The input pointer is used for reading contents of a given file location.
The output pointer is used for writing to a given file location
When we open file in read only mode, the input pointer is automatically set to
the beginning so that we can read the file from the start.
When we open a file in write only mode, the existing contents are deleted and
the output pointer is set to the beginning.
This enables us to write the file from the start
Incase we want to open an existing file to add more data, the file is opened in
“append” mode
This moves the output pointer to the end of the file.
Functions for manipulation of file pointers
To move a file pointer to any desired position inside the file the file stream
classes support the following functions to manage file pointers:
seekg(): moves the get pointer to the specified location.
seekp(): moves the put pointer to the specified location.
tellg(): gives the current position of the get pointer.
tellp(): gives the current position of the put pointer.
Example
infile.seekg(10); //moves the file pointer to the byte number 10.
bytes in a file are numbered beginning from zero). Therefore the pointer will
get the 9th byte in the file.
seekg() and seekp()
Seek functions: seekg() and seekp() can also be used with two arguments as
follows
seekg(offset, refposition);
seekp(offset, refposition);
Where the parameter offset represents the number of bytes the file pointer to
be moved from the location specified by the parameter refposition.
The refposition takes some of the following three constants defined in the ios
class.
ios::beg //offset counted from the beginning of the stream
ios::cur //current position of the pointer
ios::end//end of file
Cont…
//Example: obtaining file size
#include <iostream>
#include <fstream>
using namespace std;
int main () {
long begin,end;
ifstream myfile ("example.txt");
begin = myfile.tellg();
myfile.seekg (0, ios::end);
end = myfile.tellg();
myfile.close();
cout << "size is: " << (end-begin) << " bytes.\n";
return 0;
}
Sequential input output operations
The file stream classes support a number of member functions for performing
the input and output operations on files.
put() and get(), are designed for handling a single character at a time.
write() and read(), are designed to write and read block of binary data.
the values are stored in the disk file in the same format in which they are
stored in the internal memory.
Cont…
Binary format: 00001010 00100010
2 5 9 4
Character format:
The binary format is more accurate for storing the numbers as they are
stored in the exact format as they are stored in the exact internal
representation
There is no conversion while saving the data – much faster.
The binary I/O functions takes the following form:
Infile.read((char *)&V, sizeof(V));
Infile.write((char *)&V, sizeof(V));
The first argument is the address of the variable V, and the second one
is the length of that variable in bytes.
The address of the variable must be cast to type char *.
Text files
Text file streams are those where the ios::binary flag is not included in their
opening mode.
These files are designed to store text and thus all values that are input or output
from/to them can suffer some formatting transformations, which do not
necessarily correspond to their literal binary value.
Writing operations on text files are performed in the same way we operated with
cout.
Reading from a file can also be performed in the same way that we did with cin.
In next example reads a text file and prints out its content on the screen. We
have created a while loop that reads the file line by line, using getline.
The value returned by getline is a reference to the stream object itself,
which when evaluated as a boolean expression is true if the stream is ready
for more operations, and
false if either the end of the file has been reached or if some other error
occurred.
Cont…
string line;
// writing on a text file ifstream infile
#include <iostream> ("example.txt");
#include <fstream> if(infile.is_open())
using namespace std; {
int main () while(getline
{ (infile,line) )
ofstream outfile("example.txt"); {
if (outfile.is_open()) cout<<line<<'\n';
{ }
outfile << "This is line1.\n"; infile.close();
outfile << "This is line2.\n"; }
outfile.close(); else cout << "Unable
} to open file";
else return 0;
cout<<"Unable to open file"; }
Output:
This is line 1.
This is line 2.
Binary files
For binary files, reading and writing data with the extraction and insertion
operators (<< and >>) and functions like getline is not efficient, since we do
not need to format any data and data is likely not formatted in lines.
File streams include two member functions specifically designed to read and
write binary data sequentially: write and read.
write is a member function of ostream (inherited by ofstream).
read is a member function of istream (inherited by ifstream).
Their prototypes are:
write ( memory_block, size );
read ( memory_block, size );
Where memory_block is of type char* (pointer to char), and represents the
address of an array of bytes where the read data elements are stored or from
where the data elements to be written are taken.
The size parameter is an integer value that specifies the number of
characters to be read or written from/to the memory block.
Reading and writing class objects
The class objects are the central elements of the C++, it is quite natural that the
language supports features for writing and reading from the disk files objects
directly.
The binary I/O functions write() and read() are designed to do exactly this job.
The functions read() and write() handle the entire structure of an object as a
single unit, using the computers internal representation of data.
For instance, the function, write copies the class object from memory byte by
byte with no conversion.
Note: Only data members are written to the disk file and the member functions
are not.
Example :
The program in next slide illustrates how class objects are written to and read
from disk files.
The length of the object is obtained using the sizeof()operator.
This length represents the sum total length of all the data members of the
object.
The program uses “for” loop for reading and writing objects.
This is possible because we know the exact number of objects in the file.
In this case the length of the file is not known, we can determine the file size
in terms of objects with the help of the file pointer functions and use it in the
“for” loop approach to decide the end of file.
#include <iostream>
#include <fstream>
using namespace std;
class personType{
char name[25];
int age;
public:
personType();
void getPerson();
void printPerson();
};
personType :: personType(){
strcpy(name, "Unknown");
age = 0;
}
void personType :: getPerson() {
cout<<"Enter the name of the person: ";
cin>>name;
cout<<"Age of the person:";
cin>>age;
}
void personType :: printPerson()
{
cout<<name<<"\t\t\t"<<age;
}
int main ()
{
fstream iofile;
personType person[3]; //An array of three
persons
iofile.open("People.dat",ios::app|ios::in|
ios::out|ios::binary);
//read person
for(int i = 0; i < 3; i++)
{
person[i].getPerson();
iofile.write((char*)
&person[i],sizeof(person[i]));
}
//Print list of persons from file
cout<<"\nList of registered Persons from the file";
cout<<"\nSNo.\tName \t\t\tAge";
iofile.seekg(0,ios::beg);
for(int i = 0; i < 3; i++){
iofile.read((char*) &person[i],sizeof(person[i]));
cout<<"\n"<<(i+1)<<"\t";
person[i].printPerson();
}
iofile.close();
return 0;
}
Cont...
Updating a file: Random access
Updating is a routine task in the maintenance of any data file.
Displaying the contents of the file
Modifying an existing item
Adding a new item
Deleting an existing item
These actions require the file pointers to move to a particular location that
corresponds to the item/object under consideration.
This is implemented if the file contains a collection of items/objects of equal
lengths and the size of each object can be obtained using the statement
int obj_length = sizeof(object);
Cont…
The location of the desired object may be obtained as follows.
int location = m*obj_length;
The location gives the byte number of the first byte of the mth object.
now we can set the file pointer to reach this byte with the help of seekg() or
seekp().
To find the total number of objects in file using the obj_length as follows:
int n = file_size/obj_length;
The file_size can be obtained using the function tellg() or tellp() when the file
pointer is located at the end of the file.
To modify an object, we should reach to the first byte of that object and this is
achieved through as follows:
int loc = (object-1) * sizeof(item);
inoutfile.seekp(loc);
Error handling during file operations
The following member functions exist to check for specific states of a stream
(all of them return a bool value)
1. bad() :- Returns true if a reading or writing operation fails. For example,
in the case that we try to write to a file that is not open for writing or if the
device where we try to write has no space left.
2. fail() :- Returns true in the same cases as bad(), but also in the case that a
format error happens, like when an alphabetical character is extracted
when we are trying to read an integer number.
3. eof() :- Returns true if a file open for reading has reached the end.
4. is_open() :- To check if a file stream was successful opening a file. It
returns true if file stream is opened. Otherwise it returns false;
5. good() :- It is the most generic state flag: it returns false in the same cases
in which calling any of the previous functions would return true. Note that
good and bad are not exact opposites (good checks more state flags at
once).
The member function clear() can be used to reset the state flags.
Here is a simple program to
summarize the chapter!!!
This program uses the “People List.dat”
file to perform the following operations:
Add new person to file
Modify/Update the details of a person
Display the contents of the file
Page-42
#include <iostream>
#include <fstream>
#include<conio.h>
#include<cstring>
using namespace std;
class personType
{
char name[25];
int age;
public:
personType();
void getPerson();
void printPerson();
bool isRegistered(char pn[]);
Page-43
personType :: personType(){
strcpy(name, "Unknown");
age = 0;
}
void personType :: getPerson(){
cout<<"\nEnter Name: ";
cin>>name;
cout<<"\nEnter Age: ";
cin>>age;
}
void personType :: printPerson()
{
cout<<strupr(name)<<"\t\t\t"<<age;
}
4 Page-44
bool personType :: isRegistered(char serName[]){
if(strcmp(name, serName) == 0)
return true;
else
return false;
}
int main ()
{
personType person;
char pName[25], uChoice;
int countPerson = 0, loc = -1;
ofstream outfile; //outfile stream object used for //writing
data to file
ifstream infile;//infile stream object used for reading //data
from file
fstream iofile;
bool isModified = false;
4 Page-45
do{
cout<<"\n*********************************\
n";
cout<<"\n\t\tPERSON MENU"<<endl<<endl;
cout<<"1. Register New Person"<<endl;
cout<<"2. Show List of Persons"<<endl;
cout<<"3. Modify a Person"<<endl;
cout<<"4. Exit"<<endl;
cout<<"\nEnter your choice: ";
uChoice = getche();
4 Page-46
switch(uChoice)
{
case '1':
//Open a file and save the new person to
file
outfile.open("People List.dat",ios::app|
ios::out|ios::binary);
if(outfile.is_open())
{
cout<<"\n**********************************\n";
cout<<"\n\tNew Person Registration"<<endl;
//call function getPerson to add new
person
person.getPerson();
4 Page-47
//save to a file
outfile.write((char*) &person,
sizeof(person));
cout<<"\nPerson successfully saved.";
outfile.close();
}else{
cout<<"\nUnable to open file!.";
}
cout<<"\
n*************************************\n";
break;
4 Page-48
case '2'://open and read data from file
//and display the data to the output screen
infile.open("People List.dat",ios::in|
ios::binary);
if(infile.is_open())
{
cout<<"\n*************************************\n";
cout<<"\n\tLIST OF REGISTERED PERSONS"<<endl;
cout<<"\nSNo.\tNAME \t\t\tAGE";
countPerson = 0;
while(infile.read((char*) &person,sizeof(person)))
{
cout<<"\n"<<++countPerson<<"\t";
person.printPerson();
}
4 Page-49
cout<<"\n\n\tTotal Number Of
Persons:"<<countPerson;
infile.close();
}else{
cout<<"\nUnable to open file!.";
}
cout<<"\n******************************\n";
getch();
break;
case '3':
cout<<"\
n****************************************\n";
cout<<"\nName of the person to modify: ";
cin>>pName;
iofile.open("People List.dat", ios::in| Page-50
iofile.seekg(0, ios::beg);
loc = -1;
if(iofile.is_open()){
while(iofile.read((char*) &person,
sizeof(person)))
{
loc++;
isModified = person.isRegistered(pName);
if(isModified)
{
person.getPerson();
iofile.seekp(loc * sizeof(person));
iofile.write((char*)&person, sizeof(person));
break;
} Page-51
if(isModified)
cout<<"\n\n\tA person is modified.";
else
cout<<"\n\n\t"<<pName<<" is not
registered!";
iofile.close();
}else{
cout<<"\nUnable to open file!.";
}
cout<<"\
n***************************************\n";
getch();
break;
Page-52
case '4':
cout<<"\nThe program is now closing.";
getch();
break;
default:
cout<<"\nInvalid choice!";
getch();
break;
}
}while(uChoice!='4');
return 0;
}
//End of program
Page-53
5
Page-55