C programmers use pointers to manage streams for reading and writing data.
Streams are just file or hardware devices, such as a monitor or printer.
To point to and manage a file stream in C, use a data structure called FILE.
Pointers of type FILE are created just like any other variable.
#include <stdio.h> int main(void) { //create 3 file pointers FILE *pRead; FILE *pWrite; FILE *pAppend; return 0; } //end main
The code above created three FILE pointer variables called pRead, pWrite, and pAppend.
Each FILE pointer can open and manage a separate data file.
The basic components for file processing are opening, processing, and closing data files.
Opening a file should have error checking and/or handling.
Failure to test the results of a file-open will cause unwanted program results.
To open a data file, use the standard input/output library function fopen().
The fopen() function returns a FILE pointer to a FILE pointer.
#include <stdio.h>
main()
{
FILE *pRead;
pRead = fopen("file1.dat", "r");
} //end main
This program uses the fopen() function to open a data file, called file1.dat, in a read-only manner.
The fopen() function returns a FILE pointer back to the pRead variable.
The fopen() function takes two arguments: the file name, and the second argument tells fopen() how to open the file.
The following table lists a few common options for opening text files using fopen().
Mode | Description |
---|---|
r | Opens file for reading |
w | Creates file for writing; discards any previous data |
a | Writes to end of file (append) |
The code above generates the following result.
After opening a file, you should ensure that the FILE pointer was returned successfully.
To test fopen()'s return value, test for a NULL value in a condition, as demonstrated next.
#include <stdio.h>
main() //from ww w. j a v a 2 s . c om
{
FILE *pRead;
pRead = fopen("file1.dat", "r");
if ( pRead == NULL )
printf("\nFile cannot be opened\n");
else
printf("\nFile opened for reading\n");
} //end main
The following condition
if ( pRead == NULL )
can be shortened with the next condition.
if ( pRead )
If pRead returns a non- NULL, the if condition is true. If pRead returns NULL, the condition is false.
After successfully opening and processing a file, you should close the file using a function called fclose().
The fclose() function uses the FILE pointer to flush the stream and close the file.
The fclose() function takes a FILE pointer name as an argument.
fclose(pRead);
The code above generates the following result.
The following code shows how to read a file's contents and check for the file's EOF (end-of-file) marker using the functions fscanf() and feof().
The following program that reads a data file called names.dat until an end-of-file marker is read.
#include <stdio.h>
main() //from www. j a v a 2 s. c o m
{
FILE *pRead;
char name[10];
pRead = fopen("names.dat", "r");
if ( pRead == NULL )
printf("\nFile cannot be opened\n");
else
printf("\nContents of names.dat\n\n");
fscanf(pRead, "%s", name);
while ( !feof(pRead) ) {
printf("%s\n", name);
fscanf(pRead, "%s", name);
} //end loop
} //end main
After successfully opening names.dat, I use the fscanf() function to read a single field within the file.
The fscanf() function is similar to the scanf() function but works with FILE streams.
It takes three arguments: a FILE pointer, a data type, and a variable to store the retrieved value.
After reading the record, the printf() function displays data from the file.
To read multiple records, use a looping that can read all records until a condition is met.
To read all records until the end-of-file is met, use the feof() function.
Using the not operator (!), you can pass the FILE pointer to the feof() function and loop until the function returns a non-zero value when an end-of-file marker is reached.
fscanf() can also read records containing multiple fields by supplying to the second argument a series of type specifiers for each field in the record.
For example, the next fscanf() function expects to read two character strings called name and hobby.
fscanf(pRead, "%s%s", name, hobby);
The %s type specifier will read a series of characters until a white space is found, including blank, new line, or tab.
Other valid type specifiers for the fscanf() function are listed in Table 11.3.
Type | Description |
---|---|
c | Single character |
d | Decimal integer |
e, E, f, g, G | Floating point |
o | Octal integer |
s | String of characters |
u | Unsigned decimal integer |
x, X | Hexadecimal integer |
The code above generates the following result.
The following code shows how to read multiple fields.
#include <stdio.h>
main() //from w ww. ja v a 2 s. co m
{
FILE *pRead;
char name[10];
char hobby[15];
pRead = fopen("hobbies.dat", "r");
if ( pRead == NULL )
printf("\nFile cannot be opened\n");
else
printf("\nName\tHobby\n\n");
fscanf(pRead, "%s%s", name, hobby);
while ( !feof(pRead) ) {
printf("%s\t%s\n", name, hobby);
fscanf(pRead, "%s%s", name, hobby);
} //end loop
} //end main
The code above generates the following result.
The fprintf() function takes a FILE pointer, a list of data types, and a list of values to write information to a data file.
By separating each field in the record with a tab, I can easily read the same record back with the following program.
#include <stdio.h>
main() // w w w . j a v a 2 s. co m
{
FILE *pRead;
char fName[20];
char lName[20];
char id[15];
float gpa;
pRead = fopen("students.dat", "r");
if ( pRead == NULL )
printf("\nFile not opened\n");
else {
//print heading
printf("\nName\t\tID\t\tGPA\n\n");
//read field information from data file and store in variables
fscanf(pRead, "%s%s%s%f", fName, lName, id, &gpa);
//print variable data to standard output
printf("%s %s\t%s\t%.2f\n", fName, lName, id, gpa);
fclose(pRead);
} //end if
} //end main
Opening a data file using fopen() with a w argument value will erase any previous data stored in the file.
Use the a attribute to append data at the end of the file.
The code above generates the following result.
Appending information to a data file involves opening a file for writing using the a attribute in fopen() and writing data to the end of an existing file.
If the file does not exist, however, a new data file is created as specified in the fopen() statement.
The following code demonstrates appending records to an existing data file.
#include <stdio.h>
void readData(void);
main() // w w w . j a v a 2s . c om
{
FILE *pWrite;
char name[10];
char hobby[15];
printf("\nCurrent file contents:\n");
readData();
printf("\nEnter a new name and hobby: ");
scanf("%s%s", name, hobby);
//open data file for append
pWrite = fopen("hobbies.dat", "a");
if ( pWrite == NULL )
printf("\nFile cannot be opened\n");
else {
//append record information to data file
fprintf(pWrite, "%s %s\n", name, hobby);
fclose(pWrite);
readData();
} //end if
} //end main
void readData(void)
{
FILE *pRead;
char name[10];
char hobby[15];
//open data file for read access only
pRead = fopen("hobbies.dat", "r");
if ( pRead == NULL )
printf("\nFile cannot be opened\n");
else {
printf("\nName\tHobby\n\n");
fscanf(pRead, "%s%s", name, hobby);
//read records from data file until end of file is reached
while ( !feof(pRead) ) {
printf("%s\t%s\n", name, hobby);
fscanf(pRead, "%s%s", name, hobby);
}
}
fclose(pRead);
} //end readData
The code above generates the following result.
The following code uses goto and a couple of new functions (perror() and exit()) to build error handling into a file I/O program.
#include <stdio.h>
#include <stdlib.h>
main() { /*ww w .j a v a 2 s . c o m*/
FILE *pRead;
char name[10];
char hobby[15];
pRead = fopen("hobbies.dat", "r");
if ( pRead == NULL )
goto ErrorHandler;
else {
printf("\nName\tHobby\n\n");
fscanf(pRead, "%s%s", name, hobby);
while ( !feof(pRead) ) {
printf("%s\t%s\n", name, hobby);
fscanf(pRead, "%s%s", name, hobby);
} //end loop
} // end if
exit(EXIT_SUCCESS); //exit program normally
ErrorHandler:
perror("The following error occurred");
exit(EXIT_FAILURE); //exit program with error
} //end main
The exit() function, part of the <stdlib.h> library, terminates a program as if it were exited normally.
As shown next, the exit() function is common with programmers who want to terminate a program when encountering file I/O (input/output) errors.
exit(EXIT_SUCCESS); //exit program normally
or
exit(EXIT_FAILURE); //exit program with error
The exit() function takes a single parameter, a constant of either EXIT_SUCCESS or EXIT_FAILURE, both of which return a pre-defined value for success or failure, respectively.
The perror() function sends a message to standard output describing the last error encountered.
The perror() function takes a single string argument, which is printed first, followed by a colon and a blank, then the system generated error message and a new line.
perror("The following error occurred");
The code above generates the following result.