C/C++ File I/O
I/O Redirection
Everyone is familiar with using standard input and standard output via
cin and cout. Older C programmers are also familiar with scanf()
and prinf().

The easiest type of file I/O is executed from the command line
and is known as I/O Redirection because you redirect standard input to read from a file
instead of the keyboard and you redirect standard output to write to a file.
To use I/O Redirection to read from a file instead of from the keyboard use a command
such as myProg < IORedirTest.txt, where myProg is the name of the executable
program and IORedirTest.txt is a text file containing lines of text in the same
order and format so as to match the order and number of calls to cin. Instead
of reading input from the keyboard the program will read its' input from the file.

To use I/O Redirection to write to a file instead of to the screen use a command such
as myProg > IOOut.txt, where outputfile.txt is the name of the text file
to write to. Instead of writing to the screen output from the program will be written in
text format into this file. You can also do both at the same time with a command such as
myProg < IORedirTest.txt > IOOut.txt

Standard C File I/O
There are two types of file I/O in ANSI Standard C:
-
Standard - the most common way and the one we will look at in depth.
-
System - also called low level. The programmer must set up and keep track
of the buffer used to transfer data, the format of the data and everything else.
It is harder to do but more efficient.
Standard I/O
In Standard I/O there are four types of I/O each with its own functions:
-
Character
-
String
-
Formatted
-
Record
In order to access any of the ANSI Standard C file I/O functions you must
#include <stdio.h>. These can also be used in C++ and all of
the functions can be accessed by #include <cstdio> or #include <stdio>.
If you use either of the C++ forms of #include then you must also include using namespace std;.
Opening a file in C
The function to open a file is:
FILE *fopen(const char *filename, const char *mode);
The arguments are:
-
filename – A character array holding the name of the file to open.
This can include the path as well.
-
mode – A character array holding one of the following which tells
what you want to do with the file
-
"r" = read - file must already exist.
-
"w" = write - If the file exists its contents will be overwritten.
If not it will be created.
-
"a" = append - New data will be appended to the end of the present
data in the file if the file exists, otherwise the file will be created.
-
"r+" = read & write - file must already exist.
-
"w+" = read & write - if the file exists its contents are overwritten.
If not it is created.
-
"a+" = read & append - if file does not exist it is created.
-
"wb" = write binary
-
"rb" = read binary
-
"ab" = append binary
Most programmers use just "r", "w" or "a" and do the read and write operations in separate functions
each of which opens and closes the file. This is safer than leaving a file open while doing internal
computations.
Notice that fopen() returns a pointer of a special type called FILE.
Example:
FILE *fptr;
fptr = fopen("myfile.txt", "r");
This works fine as long as the file already exists. If not your program may crash, so the
conventional way of opening a file is:
FILE *fptr;
if ( (fptr=fopen("myfile.txt","r"))==NULL)
{
printf("Can't open file myfile.txt\n");
exit();
}
NOTE: If you want to avoid overwriting a file that already exists you can do the
check for != NULL and if the result is true give a warning that the file already exists.
Closing a file in C
To close a file in C call the function fclose(fptr); passing it the pointer to
the open file.
Reading and Writing Characters in ANSI Standard C
In Character I/O data is read or written one character at a time. The functions to use are:
-
char fputc(int, FILE *); Writes one character to a file. Note the character is treated
as an integer.
-
char fgetc(FILE *); Reads one character from a file
Sample Code
#include <stdio.h>
int main(void)
{
FILE *fptr; // pointer to FILE
char ch;
//-----------------------------------------------
// Writing characters to a file
// I/O function: int fputc(int ch, File *fptr)
//-----------------------------------------------
// Open the file for writing
fptr=fopen("textfile.txt","w");
// Write characters entered from the keyboard till user presses Enter
while ( (ch = getc(stdin)) != '\n')
fputc(ch, fptr);
// Close the file
fclose(fptr);
//-----------------------------------------------
// Reading characters from a file
// I/O function: int fgetc(File *fptr)
//-----------------------------------------------
if( (fptr = fopen("textfile.txt","r")) == NULL)
{
printf("Can't open textfile.txt.\n");
return 0;
}
while ( (ch = fgetc(fptr)) != EOF)
printf("%c", ch);
fclose(fptr);
return 0;
}
Note: EOF = -1, this is not a character stored on the disk. It is a flag transmitted by the
operating system when it realizes it has read the last character in a file. Since all ASCII
characters have an integer value of 0...255 then -1 is unique and can be recognized as EOF.
This is also why ch is read by getc() as an int type not a char type.
Reading and Writing Strings in ANSI Standard C
In String I/O data is read or written one line at a time. The functions to use are:
-
int fputs(const char*, FILE *); Writes a string to a file.
-
char *fgets(char *s, int n, FILE *); Reads at most n-1 characters into char
array s and terminates it with a '\0'. NOTE: The newline character '\n' is read in
as part of the string and must be explicitly removed.
Sample Code
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *fptr; // pointer to FILE
char string[81]; // Use 80 char lines
fptr = fopen("textfile.txt","w");
//-----------------------------------------------
// Writing strings to a file
// I/O function: int fputs(char *string, File *fptr)
//-----------------------------------------------
while (strlen(gets(string)) > 0)
{
fputs(string, fptr); // write string
fputs("\n", fptr); // write newline
}
fclose(fptr);
//-------------------------------------------------------------
// Reading strings from a file
// I/O function: char *fgets(char *string, int n, File *fptr)
//--------------------------------------------------------------
if((fptr = fopen("textfile.txt","r"))==NULL)
{
printf("Can't open textfile.txt.\n");
return 0;
}
while (fgets(string,80,fptr) != NULL)
{
string[strlen(string)-1] = '\0';
printf("%s\n", string);
}
fclose(fptr);
return 0;
}
Note: When writing lines to the file that the '\0' is not written to the file so you must put in a
newline character to separate the strings.
Note: When reading lines from the file, you have to explicitly removed the newline character from
the string as it is read.
Reading and Writing Formatted I/O in ANSI Standard C
In Formatted I/O data is read or written in a formated form just the same form as scanf() and printf().
The functions to use are:
-
fprintf(ptr,"format specifier", varlist); - Writes specified variables to disk
-
fscanf(ptr,"format specifier", varlist); - Reads specified variables from disk
Sample Code
#include <stdio.h>
#include <string.h>
int main(void)
{
int inum;
float fnum;
char string[81];
FILE *fptr;
fptr = fopen("textfile.txt", "w");
//-----------------------------------------------------------
// Writing formatted data to a file
// I/O function: int fprintf(File *fptr, char *format,...)
//-----------------------------------------------------------
do
{
printf("Type int, float, and string: ");
scanf("%d %f %s", &inum, &fnum, string);
fprintf(fptr,"%d %f %s\n",inum,fnum,string);
}
while (inum != 0);
fclose(fptr);
//-----------------------------------------------------------
// Reading formatted data from a file
// I/O function: int fscanf(File *fptr, char *format,...)
//-----------------------------------------------------------
if((fptr=fopen("textfile.txt","r"))==NULL)
{
printf("Can't open textfile.txt.\n");
return 0;
}
while(fscanf(fptr,"%d %f %s", &inum, &fnum, string) != EOF)
printf("%d %f %s\n",inum,fnum,string);
fclose(fptr);
return 0;
}
Note: This writes the data out in text format so you can read it with any word processor.
Reading and Writing Record (binary) in ANSI Standard C
In Record I/O data is read or written a whole block of memory at a time. The functions to use are:
-
fread(&object, sizeof(object), n, fptr); Reads n blocks of size sizeof(object)
into object. Unless object is an array n=1.
-
fwrite(&object, sizeof(object), n, fptr); Writes n objects of size sizeof(object)
starting at &object.
Sample Code
This example reads simple structures from keyboard and writes to disk. Then reads simple structures
from disk and prints to screen.
#include <stdio.h>
struct simple
{
int num;
char ch;
};
int main(void)
{
FILE *fptr; // pointer to FILE
simple sim;
char ch;
fptr=fopen("structfile.bin", "wb");
//------------------------------------------------------
// Writing structures to a file
// I/O function: long fwrite(void *buffer, long size,
// long numObjects, FILE *fp)
//------------------------------------------------------
do
{
printf("Type an int and a char.\n");
scanf("%d %c", &sim.num, &sim.ch);
fwrite(&sim, sizeof(struct simple), 1, fptr);
printf("Add another structure to disk(y/n)? ");
fflush(stdin); // Flush the keyboard buffer
ch = getc(stdin);
}
while(ch =='y');
fclose(fptr);
//------------------------------------------------------
// Reading structures from a file
// I/O function: long fread(void *buffer, long size,
// long numObjects, FILE *fp)
//------------------------------------------------------
if( (fptr = fopen("structfile.bin", "rb")) == NULL)
{
printf("Can't open structfile.bin.\n");
return 0;
}
while (fread(&sim, sizeof(struct simple), 1, fptr)==1)
printf("%d %c\n", sim.num, sim.ch);
fclose(fptr);
return 0;
}
Note: fread() returns an integer value equal to the number of blocks read, ie. if you ask for one
structure and fread() returns 0 you know you have reached the end of the file.
Random Access files
With files of structures you can access the stored structures randomly using the fseek() function.
fseek() uses a "file pointer", ie. it points to the next position to be read from or written to
in the file. Don't confuse this with pointers to type FILE such as *fptr.
Syntax:
fseek(fptr, offset, mode);
The arguments to fseek are:
-
fptr - A pointer to an open FILE.
-
offset - Number of bytes to move the internal file pointer, declared as a long.
-
mode - The direction in which to move.
-
0 - Seek from the beginning of the file
-
1 - Seek from the current position of the file pointer. Use negative values
to seek backward.
-
2 - Seek from the end of the file. Use negative values
to seek backward.

Example Calculating offset:
long offset;
offset = recno * sizeof(struct simple); // recno = # of record wanted
Remember records are numbered from 0 so if you want to fseek() to the first record then recno=0.
Examples:
offset = 5 * sizeof(struct simple);
fseek(fptr, offset, 0); // Find record #6
Here is an example which also checks to be sure that record exists.
offset = 5 * sizeof(struct simple);
if (fseek(fptr, offset, 0) != 0)
{
printf("Record does not exist.");
exit();
}
fread(&sim, sizeof(struct simple), 1, fptr);
Error Handling
There are a couple of handy error handling functions that you might find useful in file handling.
-
ferror(fptr); Takes a pointer to a file as its argument and returns 0 (TRUE) if
no error occured, or non-zero (FALSE) if some error occurred during the last access of the file.
-
perror("Error message"); Takes a string that you supply which usually indicates
where the error occurred. It prints this string and then it prints a system level error message.
For example it can be used after a read or write operation.
if ( ferror(fptr) ) // If error
{
perror("Disk access error!"); // Report it
fclose(fptr); // Close file
exit(); // And exit.
}
Standard C++ File I/O
C++ introduces the concept of streams. Streams are just a “flow” of characters.

All stream classes are derived from the base class ios_base and its'
immediate descendent ios. From these two superclasses are derived.
One is istream which becomes the parent class of all input streams and
from which standard input cin is derived. The other is ostream
which becomes the parent class of all output streams and from which standard output
cout is derived.

Similar to C file I/O, C++ file I/O has three basic types:
-
Character
-
String
-
Binary
In order to access any of the ANSI Standard C++ file I/O functions you must
#include <iostream.h>. These can also be accessed by #include <iostream>.
If you use the name without the .h then you must also include using namespace std;.
Opening a file in C++
Any stream object contains a member function called, appropriately, open. It can take one
of three forms:
fstream open(const char *filename, int mode);
ifstream open(const char *filename); // Open for input by default
ofstream open(const char *filename); // Open for output by default
The arguments are:
-
filename – A character array holding the name of the file to open.
This can include the path as well.
-
mode – A character array holding one of the following which tells
what you want to do with the file. This argument is only needed for fstream
objects. An ifstream (input file stream) object is opened
for input automatically and an ofstream (output file stream)
object is opened for output automatically.
-
ifstream::in = read – default for ifstream objects
-
ios::in = read – when using an fstream file reference
-
ostream::out = write – default for ostream
-
ios::out = write – when using an fstream file reference
-
ios::app = append - New data will be appended to the end of the
present data in the file if the file exists, otherwise the file will be created.
-
ifstream::binary = read binary
-
ofstream::binary = write binary
Examples: Any of these will open a file for writing
Given ofstream outfile then use...
outfile.open("myfile.txt", ofstream::out);
or
outfile.open("myfile.txt", ios::out);
or
outfile.open("myfile.txt");
Given fstream outfile then use...
outfile.open("myfile.txt", ofstream::out);
or
outfile.open("myfile.txt", ios::out);
It is a good idea to check and see if the file was successfully opened, like this:
if (!outfile.is_open())
{
cout << "Can't open file myfile.txt\n";
exit();
}
Any of these will open a file for reading
Given ifstream infile then use...
infile.open("myfile.txt", ofstream::in);
or
infile.open("myfile.txt", ios::in);
or
infile.open("myfile.txt"); As will these
Given fstream infile then use...
infile.open("myfile.txt", ofstream::in);
or
infile.open("myfile.txt", ios::in);
As with opening for write check for successful opening for reading:
if (!infile.is_open())
{
cout << "Can't open file myfile.txt\n";
exit();
}
Checking State Flags
The use of infile.is_open checks the "open" status of the fstream object. There are a number of
other flags you can check.
-
bad() Returns true if a reading or writing operation fails. This can happen if you
try to write to a file that is not open for writing or if the disk you are writing to becomes full.
-
fail() Returns true in the same cases as bad() but also if a data format error is
encountered. This can happen if you read an alphabetic character when you were expecting
to read a digit.
-
eof() Returns true if a file open for reading has reached the end.
-
good() A generic state flag. Returns false in any of the cases in which the
previous flags return true.
Note: If any of these flags gets set you must clear it before attempting any more reads/writes on the file.
Do this by calling fstreamobject.clear(). It requires no parameters.
Closing a file
To close a file after reading or writing use the close() member function:
outfile.close();
infile.close();
Reading and Writing Characters in ANSI Standard C++
In Character I/O data is read or written one character at a time. The functions to use are:
-
outfile.put(char ch); Writes one character to a file. Note the character is treated
as an integer.
-
infile.get(); Reads one character from a file
Sample Code
#include <fstream.h>
int main(void)
{
ofstream outfile; // Output file stream
ifstream infile; // Input file stream
char ch;
outfile.open("textfile.txt", ofstream::out);
//-----------------------------------------------
// Writing characters to a file
// I/O function: outfile.put(char ch)
//-----------------------------------------------
cin.get(ch);
while(ch != '\n')
{
outfile.put(ch);
cin.get(ch);
}
outfile.close();
//-----------------------------------------------
// Reading characters from a file
// I/O function: char infile.get()
//-----------------------------------------------
infile.open("textfile.txt", ifstream::in);
cout << "Characters read from file.\n";
while (infile.good())
{
ch = infile.get();
cout << ch;
}
cout << "\n";
infile.close();
return 0;
}
Reading and Writing Strings in ANSI Standard C++
In String I/O data is read or written one line at a time. The functions to use are:
-
outfile.write(char str[], int n); Write a string of n characters
from the character array str to a file.
-
infile.getline(char str[], int n); Reads up to n characters into char
array str and terminates it with a '\0'. Stops reading if a newline character
is found. Note: Unlike the C equivalent getline() does not read the newline into
the array.
Sample Code
#include <fstream.h>
int main(void)
{
ofstream outfile; // Output file stream
ifstream infile; // Input file stream
char line[81]; // Use 80 char lines
outfile.open("textfile.txt", ofstream::out);
//-----------------------------------------------------------
// Writing strings to a file
// I/O function: outfile.write(char *string, int numChars)
//-----------------------------------------------------------
cin.getline(line, 80);
while(strlen(line) > 0)
{
outfile.write(line, strlen(line)); // write string
outfile.write("\n", 1); // write newline
cin.getline(line, 80);
}
outfile.close();
infile.open("textfile.txt", ifstream::in);
//-----------------------------------------------------------
// Reading strings from a file
// I/O function: infile.getline(char *string, int maxChars)
//-----------------------------------------------------------
while (infile.good())
{
infile.getline(line, 80);
cout << line << "\n";
}
infile.close();
return 0;
}
Reading and Writing binary in ANSI Standard C++ using << and >>
The input (<<) and output (>>) operators have been overloaded for
binary input and output in C++ file I/O.
Sample Code
#include <fstream.h>
int main(void)
{
ofstream outfile; // Output file stream
ifstream infile; // Input file stream
int num;
outfile.open("binfile.dat", ofstream::binary);
//-----------------------------------------------
// Writing numerical data as binary to a file
// I/O function: Overloaded << operator
//-----------------------------------------------
for(int i=0; i<10; i++)
{
outfile << i << " \n";
}
outfile.close();
infile.open("binfile.dat ", ifstream::binary);
//-----------------------------------------------
// Reading numerical data as binary from a file
// I/O function: Overloaded >> operator
//-----------------------------------------------
while (infile.good())
{
infile >> num;
if(infile.good()) cout << num << " \n";
}
infile.close();
return 0;
}
Reading and Writing binary in ANSI Standard C++ using write and read
Binary I/O in C++ can also be used to write and read records in which
data is read or written a whole block of memory at a time. The functions to
use are:
-
write(&object, sizeof(object)); Writes 1 object of size sizeof(object)
starting at address &object in memory.
-
read(&object, sizeof(object)); Reads 1 block of size sizeof(object)
into object.
Sample Code
#include <fstream.h>
struct simple
{
int x;
char ch;
};
int main(void)
{
ofstream outfile; // Output file stream
ifstream infile; // Input file stream
simple sim1;
outfile.open("StructBin.dat", ofstream::binary);
if(!outfile.is_open())
{
cout << "Unable to open file for output."
<< "Program terminating.\n";
return 0;
}
//--------------------------------------------------------
// Writing structures in binary to a file
// I/O function: outfile.write(const char *, int size)
//--------------------------------------------------------
for(int outst=0; outst<10; outst++)
{
sim1.x = outst;
sim1.ch = 'a' + outst;
// Write 1 structure to the file. Note: the write() function
// expects a const char* as the first argument. Use the
// reinterpret_cast operator to cast the structure pointer to a
// const char*.
outfile.write(reinterpret_cast<const char *>(&sim1),
sizeof(simple));
}
outfile.close();
infile.open("StructBin.dat", ifstream::binary);
if(!infile.is_open())
{
cout << "Unable to open file for input." <<
"Program terminating.\n";
return 0;
}
//--------------------------------------------------------
// Reading structures in binary from a file
// I/O function: infile.read(char *, int size)
//--------------------------------------------------------
while(!infile.eof())
{
// Read 1 structure from the file. Note: the read() function
// expects a char* as the first argument. We use the
// reinterpret_cast operator to cast the structure pointer to a
// char*.
infile.read(reinterpret_cast<char *>(&sim1),
sizeof(simple));
if(!infile.eof())
cout << sim1.x << " -- " << sim1.ch << "\n";
}
infile.close();
return 0;
}
Random Access Files in C++
All stream objects have at least one internal stream "pointer" which marks the next position
in the file where reading or writing will occur. Input file objects (ifstream and istream)
have a get pointer that points to the next point in the file to be read from.
Output file objects (ofstream and ostream) have a put pointer that points to the next
point in the file to be written to. Usually this refers to the end of the file. An fstream
object can have both.
The functions to call to get the locations in a file are:
-
tellg() Returns the position in bytes from the beginning of the file
where the get pointer is located.
-
tellp() Returns the position in bytes from the beginning of the file
where the put pointer is located.
The functions to call to change the locations in a file are:
-
seekg() Sets the position in bytes from the beginning of the file where the
get pointer will be moved to.
-
seekp() Sets the position in bytes from the beginning of the file where the
put pointer will be moved to.
You can also move the pointers from their current location to an offset in bytes by specifying
the number of bytes to move and the position to move from.
-
seekg(offset, direction)
-
seekp(offset, direction)
...where direction is one of the enum values:
-
IOS::beg Offset is counted from the beginning of the file.
-
IOS::cur Offset is counted from the current position.
-
IOS::end Offset is counted from the end of the file.
Example code calculating the size in bytes of a file
// obtaining file size
#include <iostream>
#include <fstream>
using namespace std;
int main ()
{
long begin,end;
ifstream myfile;
myfile.open("example.txt");
begin = myfile.tellg();
myfile.seekg (0, ios::end);
end = myfile.tellg();
myfile.close();
cout << "size is: " << (end-begin) << " bytes.\n";
return 0;
}
Passing streams to functions
Frequently you need to pass a file stream into a function. Care must be taken in
doing this. File streams must be passed to functions by reference, not by value, i.e.
the function prototypes must look like this…
void myFileFunction(ifstream &fp, …)
not like this…
void myFileFunction(ifstream fp, …)
Warnings:

-
If you pass streams by value the compiler will not complain.
-
But, if you do mysterious, bad things will start to happen, and often in parts
of the code which don’t appear to be related to the offending function.