This page was last updated August, 2004
File I/O
This page address the tasks of reading and writing data on disk. In
each case file operations consist of a 3-step process: Open, Access,
Close. This page addresses the following types of File Access.
The outline below shows the relationship of all the I/O types. Click
on any one to jump directly to an explanation and examples of that type
or scroll down to read about all the types in sequential order.
-
Standard C file access
-
Opening and closing files in C
-
Standard file access in C
-
Character
-
String
-
Formatted
-
System or low-level file access in C
-
Record
-
Random Access
-
Handling file I/O errors in C
-
C++ file access
-
Opening and closing files in C++
-
Standard file access in C++
-
Character
-
String
-
System or low-level file access in C++
-
Binary
You must #include <stdio.h> to access all file operations in standard C. All of
these file I/O methods can also be used in C++.
To open a file in C you use the fopen command which returns a pointer
to a FILE data type:
FILE *fopen(const char *filename, const char *mode);
filename - character array holding the name of the file to open.
This can include the path as well.
mode - character array holding one of the following which tells how
you want to open the file. Note that even though some of the modes are single
characters this argument must still be passed as a string, i.e. in double quotes.
-
"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.
Example:
Simple way:
fptr = fopen("myfile.txt", "r");
This works fine as longs as the file already exists. If not you are in trouble, so the
conventional way of opening a file is:
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 that is open call the function fclose() passing
in the pointer to the FILE as the only argument.
fclose(fptr);
In this type of file I/O single characters are read or written. This is a very
inefficient approach and should only be written if each character must be checked
in some way while reading or writing. There are two functions used. The first is
for reading a character from an open file and the second is for writing a character
to an open file.
char fgetc(FILE *); // Reads one character from a file
char fputc(int, FILE *); // Writes one character to a file.
// Note the character is treated as an integer.
Below is a simple demonstration program that opens a file for writing characters
and writes out what is typed at the keyboard until the Enter enter key is
pressed. It then closes the file and immediately reopens it for reading. All
characters are read from the file and displayed on the screen.
#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 keyboard characters 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.
This type of I/O reads and writes whole strings on single lines to a file.
There are two functions used. The first is for reading a string/line from an
open file and the second is for writing a string/line to an open 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. See how to handle this below.
int fputs(const char*, FILE *); // Writes a string to a file.
The following sample program writes strings which are typed at the keyboard
to the file. It terminates when the <>B>Return key is pressed alone.
The file is then closed and reopened to read all strings/lines from the file
and print them on the screen.
#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 the use of the string input function gets(). Also, note
that the '\0' is not written to the file so you must explicitly put
in a newline character ('\n') to separate the strings. When the string
is read back in the newline character is read in with it and becomes
a part of the string. To remove the newline character use
string[strlen(string)-1] = '\0'; which overwrites the newline
character with another null terminator ('\0').
Formatted I/O reads and writes formatted data in the same form as scanf()
and printf(). There are two functions used. The first is for reading
formatted data from an open file and the second is for writing formatted data
to an open file.
fscanf(ptr,"format specifier", varlist); // Reads specified variables from disk
fprintf(ptr,"format specifier", varlist); // Writes specified variables to disk
Note: This writes the data out in text format so you can open the file and read it
with any word processor.
The following example reads an int, a float, and a string from the keyboard and
writes them to disk. It terminate when the first number is 0. The file is then
closed and reopened to read the data back from textfile.txt and print it on the
screen.
#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 when you write to disk using formatted I/O you must explicitly include the white space delimiters
between values.
Record or binary I/O in C allows you to read and write whole blocks of data at one time.
This is usually used to read and write whole structures and arrays to disk.
There are two functions used. The first is for reading
blocks of data from an open file and the second is for writing blocks of data
to an open file.
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.
The following example reads data from the keyboard, places it in a simple data structure,
and writes the structure to disk. The program then closes the file and reopens it to reads
simple structures from disk and print them on the screen.
#include <stdio.h>
// Define a simple data structure
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("strufile.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 we ask for one structure and fread() returns 0 we know we have reached
the end of the file because there was no structure left to read.
The functions fread() and fwrite() discussed in the previous section
can also be used to create random access files. These are files in which you can
seek to a particular record in the file and read or write that one file without
touching all other records in the file. This is dependent on using records (structures)
on only one type, i.e. all the same size. With files of structures you can randomly
access the stored structures using the fseek() function.
int fseek(FILE *fptr, long offset, int mode);
fptr - pointer to FILE.
offset - the number of bytes to move from the current position in the file.
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.
mode - the direction in which to move.
Example:
-
Determining mode:
Mode Offset measured from
0 Beginning of file
1 Current position of file pointer
2 End of file
-
Calculating the offset:
long offset; // declare as long int
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.
To find the 6th record use:
offset = 5 * sizeof(struct simple);
fseek(fptr, offset, 0);
Here is an example which also checks to be sure that the 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.
-
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.
Example: Use after a read or write operation
if ( ferror(fptr) ) // If error
{
perror("Disk access error!"); // Report it
fclose(fptr); // Close file
exit(); // And exit
}
C++ introduces the concept of streams in file I/O. A stream is
just a "flow" of characters.
#include to access all file operations in standard C++
Opening a file in C++ can be done with either of the following functions:
fstream open(const char *filename, int mode);
ifstream open(const char *filename); // Open for input by default
ostream open(const char *filename); // Open for output by default
filename - character array holding the name of the file to open. This can include the path as well.
mode - Integer defining how the file is to be opened. This can be
-
ifstream::in = read - default for ifstream
-
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
ofstream outfile; // Declare an ofstream reference
outfile = open("myfile.txt", ofstream::out);
outfile = open("myfile.txt", ios::out);
outfile = open("myfile.txt");
Any of will these will also open a file for writing
fstream outfile; // Declare an fstream reference
outfile = open("myfile.txt", ofstream::out);
outfile = open("myfile.txt", ios::out);
In C++ as in C it is a good idea to check and see if the file was successfully opened.
In C++ it is done like this:
if (!outfile.is_open())
{
cout << "Can't open file myfile.txt\n";
exit();
}
Any of these will open a file for reading
ifstream infile; // Declare an ifstream reference
infile = open("myfile.txt", ofstream::in);
infile = open("myfile.txt", ios::in);
infile = open("myfile.txt");
Any of will these will also open a file for reading
fstream infile; // Declare an fstream reference
infile = open("myfile.txt", ofstream::in);
infile = open("myfile.txt", ios::in);
When opening a file for reading it is also a good idea to check
to make sure the file was successful opening:
if (!infile.is_open())
{
cout << "Can't open file myfile.txt\n";
exit();
}
Closing a file in C++
To close a file call the close() function that is part of the file object.
outfile.close();
infile.close();
In this type of file I/O single characters are read or written. This is a very
inefficient approach and should only be written if each character must be checked
in some way while reading or writing. There are two functions used. The first is
for reading a character from an open file and the second is for writing a character
to an open file.
char infile.get(); // Reads one character from a file
void outfile.put(char ch); // Writes one character to a file.
Below is a simple demonstration program that opens a file for writing characters
and writes out the alphabet. It then closes the file and immediately reopens it for reading. All
characters are read from the file and displayed on the screen.
#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)
//-----------------------------------------------
for(ch='a'; ch<='z'; ch++)
{
outfile.put(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;
}
This type of I/O reads and writes whole strings on single lines to a file.
There are two functions used. The first is for reading a string/line from an
open file and the second is for writing a string/line to an open file.
infile.getline(char string[], int n); // reads up to n characters into
// char array string and terminates it with a '\0'.
// Stops reading if a newline character is found.
// Does not read the newline.
outfile.write(char string[], int n); // writes a string of n characters to
// a file from the character array.
The following sample program writes some test strings
to the file. The file is then closed and reopened to read all strings/lines from the file
and print them on the screen.
#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)
//------------------------------------------------------------------------
for(int i=0; i<10; i++)
{
outfile.write("Testing writing to file\n", 24);
}
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;
}
There are two approaches for using binary file I/O in C++. The first uses the overloaded
operators << and >>.
The following example writes some numbers in binary format to a file. It then closes
the file and reopens it for reading. It then read the numbers in binary format from the file.
#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;
}
The second approach to binary I/O in C++ reads and writes whole structures.
In the example below we write some structures in binary format to a file.
The file is then closed and reopened for reading. Then we read the structures
in binary format from the file and print the data on the screen.
#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. We use the
// reinterpret_cast operator to cast the structure pointer to a
// *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())
{
infile.read(reinterpret_cast<char *>(&sim1),
sizeof(simple));
if(!infile.eof())
cout << sim1.x << " -- " << sim1.ch << "\n";
}
infile.close();
return 0;
}