Read Sams Teach Yourself C in 24 Hours Online
Authors: Tony. Zhang
Files Versus Streams
The C language provides a rich set of library functions to perform input and output (I/O) operations. Those functions can read or write any type of data to files. Before we go any further in discussing the C I/O functions, let’s first understand the definitions of files and streams in C.
What Is a File?
In C, a
file
can refer to a disk file, a terminal, a printer, or a tape drive. In other words, a file represents a concrete device with which you want to exchange information. Before you perform any communication to a file, you have to open the file. Then you need to close the opened file after you finish exchanging information with it.
What Is a Stream?
The data flow you transfer from your program to a file, or vice versa, is called a
stream
, which is a series of bytes. Unlike a file, which targets a specific I/O device, a stream is device independent. All streams have the same behavior regardless of whatever device they are associated with. To perform I/O operations, you can read from or write to any type of file by simply associating a stream to the file.
There are two formats of streams. The first one is called the
text stream
, which consists of a sequence of characters (that is, text data). Depending on the system, each line of characters in a text stream may be terminated by a newline character (‘\n’). Text streams are used for textual data, which has a consistent appearance from one environment to another, or from one machine to another.
The second format of streams is called the
binary stream
, which is a series of bytes, for example, the content of an executable program file. Binary streams are primarily used for nontextual data, where the exact contents of the data are maintained without regard to appearance.
Buffered I/O
In C, a memory area that is temporarily used to store data before it is sent to its destina-tion is called a
buffer
. With the help of buffers, the operating system can improve efficiency by reducing the number of accesses to I/O devices (that is, files).
Access to a disk or other I/O device is generally much slower than direct access to memory. Several I/O operations can be performed on a buffer that represents the I/O file, instead of the file itself. For example, if several write operations are sent to a buffer, they are kept in memory until it is time to save, or commit, the new data to the actual device.
27 067231861x CH21 1/25/00 11:09 AM Page 357
Reading and Writing with Files
357
(This process is known as
flushing
the buffer.) So instead of several write operations in a row, each going to the slow disk device, they all take place in the memory buffer and the disk device will only be accessed once—when the buffer is flushed.
By default, all I/O streams are buffered. Buffered I/O is also called high-level I/O, whereas unbuffered I/O (directly to the device) is called low-level I/O.
The Basics of Disk File I/O
Now let’s focus on how to open and close a disk data file and how to interpret error messages returned by I/O functions.
Pointers of
FILE
The FILE structure is the file control structure defined in the header file stdio.h. A pointer of type FILE is called a file pointer, which references a disk file. A file pointer is used by a stream to conduct the operation of the I/O functions. For instance, the following defines a file pointer called fptr:
FILE *fptr;
In the FILE structure there is a member, called the
file position indicator
, which points to the position in a file where data will next be read from or written to. You’ll learn how to move the file position indicator in the next lesson.
Opening a File
The C I/O function fopen()opens a file and associates a stream with the opened file. You need to specify the method of opening the file and the filename as arguments to the fopen() function.
The syntax for the fopen() function is
AX
#include
FILE *fopen(const char *filename, const char *mode);
YNTS
Here filename is a char pointer that references a string containing a filename. The file-
,
name is given to the file that is about to be opened by the fopen() function. mode points to another string that specifies the way to open the file. The fopen() function returns a pointer of type FILE. If an error occurs during the procedure to open a file, the fopen()
,
function returns a null pointer.
21
The mode parameter is made by a combination of the characters r (read), w (write), b (binary), a (append), and + (update). When you use append mode, and the file already exists, the contents of the file will be preserved and new data that you write will be 27 067231861x CH21 1/25/00 11:09 AM Page 358
358
Hour 21
added to the end. If the file does not already exist, it will be created. This is in contrast to w, which always attempts to create a new file, and will discard whatever data may already be in the file. Using the + character sets the mode to both reading and writing. When you use r, the file must already exist; if it does not, the call will fail.
The following list shows the possible ways to open a file by various strings of modes:
• “r” opens an existing text file for reading.
• “w” creates a text file for writing.
• “a” opens an existing text file for appending.
• “r+” opens an existing text file for reading or writing.
• “w+” creates a text file for reading or writing.
• “a+” opens or create a text file for appending.
• “rb” opens an existing binary file for reading.
• “wb” creates a binary file for writing.
• “ab” opens an existing binary file for appending.
• “r+b” opens an existing binary file for reading or writing.
• “w+b” creates a binary file for reading or writing.
• “a+b” opens or creates a binary file for appending.
Note that you might see code where the mode is given as “rb+” instead of “r+b”. These two strings are equivalent. Similarly, “wb+” is the same as “w+b”; “ab+” is equivalent to
“a+b”.
The following statements try to open a file called test.txt:
FILE *fptr;
if ((fptr = fopen(“test.txt”, “r”)) == NULL){
printf(“Cannot open test.txt file.\n”);
exit(1);
}
Here “r” is used to indicate that a text file is about to be opened for reading only. If an error occurs when the fopen() function tries to open the file, the function returns a null pointer. (This will happen if test.txt does not already exist.) Then an error message is printed out by the printf() function and the program is aborted by calling the exit() function with a nonzero value.
Closing a File
After a disk file is read, written, or appended with some new data, you have to disassociate the file from a specified stream by calling the fclose() function.
27 067231861x CH21 1/25/00 11:09 AM Page 359
Reading and Writing with Files
359
The syntax for the fclose() function is
AX
#include
int fclose(FILE *stream);
YNTS
Here stream is a file pointer that is associated with a stream to the opened file. If
,
fclose() closes a file successfully, it returns 0. Otherwise, the function returns EOF.
Normally, the fclose() function fails only when the disk is removed before the function
,
is called or there is no more space left on the disk.
Because all high-level I/O operations are buffered, the fclose() function flushes data left in the buffer to ensure that no data will be lost before it disassociates a specified stream with the opened file.
Note that a file that is opened and associated with a stream has to be closed after the I/O
operation. Otherwise, the data saved in the file may be lost; some unpredictable errors might occur during the execution of your program. In addition, failing to close a file when you are done with it may prevent other programs (or users of your program) from accessing the file later.
The program in Listing 21.1 shows you how to open and close a text file and how to check the value of the file pointer returned from fopen() as well.
TYPE
LISTING 21.1
Opening and Closing a Text File
1: /* 21L01.c: Opening and closing a file */
2: #include
3:
4: enum {SUCCESS, FAIL};
5:
6: main(void)
7: {
8: FILE *fptr;
9: char filename[]= “haiku.txt”;
10: int reval = SUCCESS;
11:
12: if ((fptr = fopen(filename, “r”)) == NULL){
13: printf(“Cannot open %s.\n”, filename);
14: reval = FAIL;
15: } else {
16: printf(“The value of fptr: 0x%p\n”, fptr);
17: printf(“Ready to close the file.”);
18: fclose(fptr);
21
19: }
20:
21: return reval;
22: }
27 067231861x CH21 1/25/00 11:09 AM Page 360
360
Hour 21
The following output displays onscreen after running the executable 21L01.exe of the program in Listing 21.1 on my computer. (Note that the value of the fptr is likely to be different on your machine. That’s okay.)
The value of fptr: 0x013E
OUTPUT
Ready to close the file.
The purpose of the program in Listing 21.1 is to show you how to open a text
ANALYSIS
file. From the expression in line 12, you can see that the fopen() function tries to open a text file with the name contained by the character array filename for reading.
The filename array is defined and initialized with the name haiku.txt in line 9.
If an error occurs when you try to open the text file, the fopen() function returns a null pointer. Line 13 then prints a warning message, and line 14 assigns the value represented by the enum name FAIL to the int variable reval. From the declaration of the enum data type in line 4, you know that the value of FAIL is 1.
If, however, the fopen() function opens the text file successfully, the statement in line 16
prints the value contained by the file pointer fptr. Line 17 tells the user that the program is about to close the file, and line 18 then closes the file by calling the fclose() function.
In line 21, the return statement returns the value of reval that contains 0 if the text file has been opened successfully, or 1 otherwise.
From the output shown on my screen, I see that the value held by the file pointer fptr is 0x013E after the text file is opened.
Reading and Writing Disk Files
The program in Listing 21.1 does not do anything with the text file, haiku.txt, except open and close it. In fact, there are two pieces of Japanese haiku, written by Sodo and Chora, saved in the haiku.txt file. So how can you read them from the file?
In C, you can perform I/O operations in the following ways:
• Read or write one character at a time.
• Read or write one line of text (that is, one character line) at a time.
• Read or write one block of characters at a time.
The following three sections explain the three ways to read from, and write to, disk files.
One Character at a Time
Among the C I/O functions, there is a pair of functions, fgetc() and fputc(), that can be used to read from or write to a disk file one character at a time.
27 067231861x CH21 1/25/00 11:09 AM Page 361
Reading and Writing with Files
361
The syntax for the fgetc() function is
AX
#include
int fgetc(FILE *stream);
YNTS
Here stream is the file pointer that is associated with a stream. The fgetc() function
,
fetches the next character from the stream specified by stream. The function then returns
,
the value of an int that is converted from the character. EOF is returned if fgetc() encounters the end of the file, or if there is an error.
The syntax for the fputc() function is
AX
#include
int fputc(int c , FILE *stream);
YNTS
Here c is an int value that represents a character. In fact, the int value is converted to an
,
unsigned char before being outputted. stream is the file pointer that is associated with a stream. The fputc() function returns the character written if the function is successful;
,
otherwise, it returns EOF. After a character is written, the fputc() function advances the associated file pointer.
To learn how to use the fgetc() and fputc() functions, let’s have a look at Listing 21.2, which contains a program that opens a text file, and then reads and writes one character at a time.
TYPE
LISTING 21.2
Reading and Writing One Character at a Time
1: /* 21L02.c: Reading and writing one character at a time */
2: #include
3:
4: enum {SUCCESS, FAIL};
5:
6: void CharReadWrite(FILE *fin, FILE *fout);
7:
8: main(void)
9: {
10: FILE *fptr1, *fptr2;
11: char filename1[]= “outhaiku.txt”;
12: char filename2[]= “haiku.txt”;
13: int reval = SUCCESS;
14:
15: if ((fptr1 = fopen(filename1, “w”)) == NULL){
16: printf(“Cannot open %s.\n”, filename1);
17: reval = FAIL;
21
18: } else if ((fptr2 = fopen(filename2, “r”)) == NULL){
19: printf(“Cannot open %s.\n”, filename2);
continues
27 067231861x CH21 1/25/00 11:09 AM Page 362
362
Hour 21
LISTING 21.2
continued
20: reval = FAIL;
21: } else {