FILE INPUT AND OUTPUT IN C


Save your data! Retrieve your data! Those two things are essential to computing. Everyone who uses a computer needs to save and load things. Most people take it for granted. But we are programmers. We take nothing for granted. The key to saving and loading is, of course, files. There needs to be a way to create, read, write to, and delete files. What kind of programming language would C be if it did not provide that functionality? So, here we will learn about working with files.

Working with files isn't much different than working with the keyboard and monitor, as you will soon see. If you want to use a file, you must open it first. It is important to note, the word "open" can also mean create. When a command is given to open a file, the operating system does all the real work, then returns a file handle to your program. That means your program must have a file handle declared to accept it. A file handle is kind of like normal variables. They are declared like this:


	FILE *myFile;
Always Type FILE in all caps, that is the variable type. Then you type an asterisk, then any name you want, just like a normal variable. Now you have a variable that can hold file handles. These are also called file pointers.

OPENING (OR CREATING) A FILE

Now that you have a file pointer you can open a file (remember that open can also mean create). This is done with the fopen function. This function will tell the operating system to open the file and it will return the file pointer. A call to fopen might look like this:

	myFile = fopen("filename", "r");
The first parameter to fopen is the file name. It can either be a string constant (in quotes), or a string variable. The second parameter is the mode you want to open the file in. The "r" means read only mode. In this mode your program can read from the file, but not write to it. More on that soon. When fopen is done it will return a pointer to the file, which is stored in myFile. It can then be used to access the file in other functions. Back to the file modes. There are several, so here is a table listing them, and their effects.

MODE USED FOR FILE CREATED? EXISTING FILE?
"a" Appending Yes Appended to
"a+" Reading and appending Yes Appended to
"r" Read only No If not found fopen returns NULL
"r+" Reading and writing No If not found fopen returns NULL
"w" Write only Yes Destroyed
"w+" Reading and writing Yes Destroyed
fopen file modes

If you want to add to a file that might already have stuff in it, then you want one of the appending modes (a, a+). If you want to read and/or write to a file only if it already exists, then you want to use a reading mode (r, r+). If you want to write to a file, but overwrite any existing data, then use a writing mode (w, w+). For more information see the Function fopen page.

WRITING TO FILES

At this point there is an open (possibly new) file, and pointer to that file. There are several functions to manipulate files, and most of them are VERY similar to the I/O functions we already know. For example there is fprintf and fscanf. Those are the main functions discussed here.

To write to a file, use fprintf. It works just like printf, but there is an extra parameter, the file pointer. A call to fprintf might look like this:


	fprintf(myFile, "I'm writing this to a file %d", x);
In this example, myFile is the file pointer to some file that has already been opened for writing. Everything else is just the same as a normal printf. The result is that this line is now written to a file. Here is a simple file I/O program to see this stuff all together.

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int i, score[10];
   FILE *HiScores;

   for(i=0; i<10; i++)
      score[i] = 100-i;

   HiScores = fopen("Hi.scr", "w");

   if( HiScores == NULL )
   {
      puts("Error opening file Hi.scr");
      exit(0);
   }

   for( i=0; i<10; i++ )
      fprintf( HiScores, "Number %d:\t %d\n", i+1, score[i]);

   return 0;
}


If you compile and run this program it might look like nothing happened. But if look in your directory (the same one you ran the program from) you will find a Hi.scr file. If you view that file (I used notepad) it should look like this:
Number 1:	 100
Number 2:	 99
Number 3:	 98
Number 4:	 97
Number 5:	 96
Number 6:	 95
Number 7:	 94
Number 8:	 93
Number 9:	 92
Number 10:	 91
And there you go! At the top of the program I included the stdlib.h file only because I wanted to use the exit function. Depending on which compiler you are using you might only need stdio.h. This program runs on three variables, i for loop control, an array of integers (so I guess it's actually 12 variables), and a file pointer called HiScores.

The first for loop just initializes the elements of the score array. Next, HiScores is set equal to the results of an fopen function. In this function call we simply hard code the file name in, and tell it that we want to write to this file, and if it already exists, wipe it out! The operating system will return a file pointer which will be stored in HiScores

The following if statement is good error checking. This should always be done when working with files. Remember, if the OS can't open the file it returns NULL. So, if HiScores equals NULL then there was an error. The user should be notified, then take whatever action you feel is appropriate. I just exit the program to make things simple.

The last for loop is where all the good stuff happens. For each element in the array fprintf is called and a new line is written to the file. Notice that all the normal formatting and conversion stuff works just fine with fprintf. It is a little messy so you have be careful typing it in, but the file looks just like your screen would if printf had been used. Click to see the fprintf page.

READING FROM FILES

Just like fprintf is not much different than printf, fscanf is not much different than scanf. In fact, the only difference is the extra parameter, the file pointer. And of course, fscanf reads from a file, not the keyboard. Here is an example of fscanf assuming we already have a file pointer called myFile.

	fscanf( myFile, "%d%d", &x, &y);
In this example two integers will be read from a file, and stored in the variables x and y. The integers must be separated by a space or a line feed (enter). Yes, the ampersands are still needed.

There is an important difference between scanf and fscanf that I didn't mention above. That is, scanf has to wait for the user to enter the data. If the user never enters anything, or not enough, the program waits. On the other hand fscanf knows it can just go fetch the data. The program will continue whether enough data was read in or not. As I'm sure you can imagine that can cause havoc in your program. If you remember from the scanf page the function returns an integer value equal to the number of things it read in. The same is true for fscanf. Checking this value can be extremely helpful since you may not know if the file really has enough stuff in it. This error checking, along with fscanf is demonstrated in the following sample program!

First create a file called info.txt and make sure it is in the same directory as your program. In the file, type in a name, press enter, then a number. Actually, you might want to wait until you have already run the program once (so you can see the error checking in action).



#include <stdio.h>
#include <stdlib.h>

int main()
{
   char name[10];
   char FileName[10] = "info.txt";
   int age;
   FILE *infoFile;

   infoFile = fopen(FileName, "r");

   if( infoFile == NULL )
   {
	   printf("Error opening file: %s", FileName);
	   exit(0);
   }

   if( fscanf( infoFile, "%s%d", name, &age) != 2 )
   {
	   puts("Error reading from file!");
	   exit(0);
   }

   printf("%s is %d years old!\n", name, age);
   puts("Program finished successfully!");

   return 0;
}


The first time I ran it without creating the file. . .

Error opening file: info.txt
Then with just the name:

Error reading from file!
Finally with everything right:

Skippy is 99 years old!
One of the first differences in this program is that we used a string variable to hold the name. Really its still hard coded in, but we could have asked the user to type in a file name with good old scanf. This time the file was opened in read only mode (fopen(FileName, "r");), because that's all we want to do is read. The first if statement is basically the same error check we did in the last program except now we use printf to display the file name.

The next if statement is the beef of the program. As you can see, fscanf is called inside the if statement, then compared to two with a not equal relational operator (!=). Remember the section on nesting function calls, the innermost function call is executed first, then its return value is used in its place. As was just mentioned above, the return value is the number of things read. We want it to read two things, so if the value is not two the program should not go on. That is what happens here. The fscanf function is executed then its return value is compared to two. If it is two, then great, we have our data, if not the program quits.

All that's left is to display the data with a standard printf. There is of course, a fscanf page.

THE END OF FILE (EOF) THINGY

What if you didn't know how many things where in a file, but you want to read them all. Every file has what is called an EOF marker. The EOF stands for End Of File. And fscanf will return this value when it reaches the end of a file. All you need is a nice while loop. Here is yet another sample program, but to keep things simple there is no error checking. First create a file called ints.txt, and put in some integers, as many as you want!

#include <stdio.h>

int main()
{
   int i;
   FILE *fp;

   fp = fopen("ints.txt", "r");

   while( fscanf( fp, "%d", &i) != EOF )
	   printf("%d\n", i);

   return 0;
}

My output:

10
20
30
40
Again, the fscanf function is executed, then compared to EOF. When the return value equals EOF the while loop halts. Of course, a better program would have some error checking in it.

FCLOSING FILES

There is one thing that all of the above sample programs are missing. They do not close the file. Sure, we can get away with that, but it is better practice to close files when finished with them. Closing the file ensures that every last bit gets written to the file. That means it is possible that something will not get written to a file if it is not closed properly. Personally I have never had a problem with it; it's just good programming to close them. All you need to do is call fclose. This function requires the file pointer as a parameter and nothing else. Here is an example where myFile is a file pointer.

	fclose( myFile );
All of the sample programs above should really end with this line. If you really want to see it, there is an fclose page. There are several more file I/O functions in C, but for now I think these will do.

TECHNICAL NOTES

*The FILE in a file pointer declaration is actually a macro (one of those #define things).
*EOF is a #define also. I believe it is defined as -1.
*As far as the computer is concerned the keyboard and monitor are files too!

Back to table of contents

Copyright © 2002 by Anthony R. Mantoan