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.
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 |
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.
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;
}
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: 91And 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.
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;
}
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.
#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;
}
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.
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.
Copyright © 2002 by Anthony R. Mantoan