Part 10: File Access
Many programs are designed to access and manipulate data. Some say this is the most common use for programs. When the loading, changing and making use of the data is all done, what do you do with that data? If you just exit, clearing the data from memory, you have to reenter it again the next time you run the program. While this is not a problem for programs that access a small amount of data, imagine a database listing of all the employees where you work. Or a listing of the names, first, last and middle, of all the students in your class.
It would be very time consuming to have to reenter the information every time you ran the program that access and manipulates that data. This is where file access comes in. Through a selection of predefined functions, C allows you access to read from and write to files on a floppy disk or hard drive. This makes it easy to save the data you want to for later use.
The first thing you need to do in order to access a file is to open that file. To open a file you use the function fopen(). The fopen() function returns a pointer variable to the file you have opened and this pointer allows you to read or write data to the file you opened, depending on the arguments you used in the fopen() function. Every file your program opens, you must close when you are done accessing it. To close a file you use the fclose() function. Here is a simple example that opens then closes a file:
/* fileaccess.c 1.0 (06.20.98) */
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *file_p;
char FileName[20] = "Testfile.txt";
file_p = fopen(FileName, "r");
if (file_p)
{
fclose(file_p);
}
return 0;
}
This small example opens the file Testfile.txt for reading. The "r" argument in the fopen() function indicates to open for reading. If the file exists it will return a pointer to the file and store it in the FILE pointer file_p. If the file cannot be opened, file_p is given the value of NULL or 0. If the file was opened and the value in file_p is not 0 the program will then close the file.
Other modes available are "w" and "a" for writing and appending. Opening a file for writing destroys any contents of the file if the file already exists and starts a new file. Appending allows you to add information on the end of the file without losing its current contents. You should check if a file exists before opening for writing unless you are sure you want to lose the current contents. You can do that like this:
/* filecheck.c 1.0 (06.20.98) */
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *file_p;
char FileName[20] = "Testfile.txt";
if(!(file_p = fopen(FileName, "r")))
{
file_p = fopen(FileName,"w");
}
fclose(file_p);
return 0;
}
This short example tries to open the file Testfile.txt. If it cannot open the file, it creates the file by opening it for writing the closes the file. If it can open the file then it simply closes the file. Now that you have the basic idea how to check for a file before opening for writing which would destroy the file, you can create your own function that you can use in your programs to do this. Here is an example of how it could be written and used:
/* filecheck.c 2.0 (06.20.98) */
#include <stdio.h>
BOOL exists(char *FileName);
int main(int argc, char *argv[])
{
FILE *file_p;
char FileName[20] = "Testfile.txt";
if(!(exists(FileName)))
{
file_p = fopen(FileName,"w");
fclose(file_p);
}
return 0;
}
BOOL exists(char *FileName)
{
FILE *file_p;
if(file_p(FileName,"r"))
{
fclose(file_p);
return(TRUE);
}
return(FALSE);
}
In the main() part of the program, it uses the exists() function to see if the file exists. The function exists() checks to see if it can open the file. If it can, it closes the file and returns the Boolean value TRUE telling the main() part of the program that called it that the file does exist. If exists() cannot open the file then it returns the value FALSE and since it could not open the file, there is no need to close it. Then the main() program opens the file for writing, knowing that there is no need to worry about overwriting an existing file.
Now that you are able to open and close a file, what can you do with it once it is opened? If a file is open for writing out to, you can send information out to the file using the fprintf() function, which is a variation of the printf() function discussed in a previous article. The main difference in fprintf() is that the first argument must be the pointer to the file, telling the function where to send the data to. The other arguments are the same as they were for the printf() function and the function is used in the same manner. Here is a short example, continuing to use the function exists():
/* filewrite.c 1.0 (06.20.98) */
#include <stdio.h>
BOOL exists(char *FileName);
int main(int argc, char *argv[])
{
FILE *file_p;
char FileName[20] = "Testfile.txt";
if(!(exists(FileName)))
{
file_p = fopen(FileName,"w");
fprintf(file_p, "This is some sample text and the number %d.\n",3);
fclose(file_p);
}
return 0;
}
BOOL exists(char *FileName)
{
FILE *file_p;
if(file_p(FileName,"r"))
{
fclose(file_p);
return(TRUE);
}
return(FALSE);
}
This example sends a simple line of formatted text out to the file it opens then closes the file.
If a file is opened for reading, you can read from it in a similar manner. You can use the fscanf() function, which is a variation on the scanf() function mentioned in a previous article. Like fprintf() the main difference is that the first argument must be the pointer to the file that is opened. Assuming that you compiled and ran the previous example, this example will open that file, read the contents in and display them. Since we are reading from the file we know we created, we do not need to check if the file exists before opening it.
/* fileread.c 1.0 (06.20.98) */
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *file_p;
char FileName[20] = "Testfile.txt";
char DataString[50];
if(file_p = fopen(FileName,"r"))
{
fscanf(file_p, "%s", DataString);
fclose(file_p);
printf("The string from the file is %s\n", DataString);
}
return 0;
}
This example tries to open the file Testfile.txt. If it is successful in opening the file, it reads in the first string into the array DataString. It then closes the file and displays the string that was read in.
While the fprintf() and fscanf() functions are simple to use when you know the exact information you are writing to or reading from a file, sometimes that information is not known and it is necessary to read or write data one character at a time. This is possible with the getc() and putc() functions.
The getc() function takes the file pointer as its argument, reads in the next character from the file and returns that character as a integer or EOF when it reaches the end of the file. The integer returned can easily type cast into a character variable for your use.
The putc() function's arguments are the character to be written to the file and the file pointer and it returns the character written or EOF if an error occurs. Here is a small example of getc() and putc() in use:
/* characterio.c 1.0 (06.20.98) */
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *file_p;
char FileName[20] = "Testfile.txt";
char storage;
if(file_p= fopen(FileName,"r")
{
storage = (char)getc(file_p);
fclose(file_p);
}
if(file_p= fopen(FileName,"a")
{
fseek(file_p,0,SEEK_END);
putc(storage, file_p);
fclose(file_p);
}
return 0;
}
This small example shows how to use the append or "a" mode. First if the file exists, it is opened and the first character is read and stored in the storage variable using getc() then the file is closed. The file is opened again in append mode. If the file did not exists it would have been created, but since it does exist fopen() returns a pointer to the end of the file.
While opening in append mode should always return the pointer directed at the end of the file it is safer to assume the pointer is not set properly and to set the file pointer manually to the end of the file so any output will not overwrite data already in the file. This is what the fseek() function is for. Its arguments are the pointer to the file, the number of bytes to move into the file and the starting position to move from. SEEK_END is defined in the stdio.h file and is used to mean the end of the file. You would use SEEK_SET to start at the beginning of the file and SEEK_CUR to start from the current pointer position.
After setting the file pointer at the end of the file the program puts the character from storage back into the file then closes the file.
There are still other methods to read from and write to files, including fgets(), fputs(), fread() and fwrite(). You can find details on these functions in any good C programming book, including "The C Programming language 2nd Ed." by Brian Kernighan and Dennis Ritchie, the creators of the C programming language.

