Read Sams Teach Yourself C in 24 Hours Online
Authors: Tony. Zhang
TYPE
LISTING 21.4
Reading and Writing One Block of Characters at a Time
1: /* 21L04.c: Reading and writing one block at a time */
2: #include
3:
4: enum {SUCCESS, FAIL, MAX_LEN = 80};
5:
6: void BlockReadWrite(FILE *fin, FILE *fout);
7: int ErrorMsg(char *str);
8:
9: main(void)
10: {
11: FILE *fptr1, *fptr2;
12: char filename1[]= “outhaiku.txt”;
13: char filename2[]= “haiku.txt”;
14: int reval = SUCCESS;
15:
16: if ((fptr1 = fopen(filename1, “w”)) == NULL){
17: reval = ErrorMsg(filename1);
18: } else if ((fptr2 = fopen(filename2, “r”)) == NULL){
19: reval = ErrorMsg(filename2);
20: } else {
21: BlockReadWrite(fptr2, fptr1);
22: fclose(fptr1);
23: fclose(fptr2);
24: }
25:
26: return reval;
27: }
28: /* function definition */
29: void BlockReadWrite(FILE *fin, FILE *fout)
30: {
31: int num;
32: char buff[MAX_LEN + 1];
33:
34: while (!feof(fin)){
35: num = fread(buff, sizeof(char), MAX_LEN, fin);
36: buff[num * sizeof(char)] = ‘\0’; /* append a null character */
37: printf(“%s”, buff);
38: fwrite(buff, sizeof(char), num, fout);
39: }
40: }
41: /* function definition */
42: int ErrorMsg(char *str)
43: {
27 067231861x CH21 1/25/00 11:09 AM Page 369
Reading and Writing with Files
369
44: printf(“Cannot open %s.\n”, str);
45: return FAIL;
46: }
Again, I get the same output on the screen because the program in Listing 21.4 also reads the same text file, haiku.txt:
Leading me along
OUTPUT
my shadow goes back home
from looking at the moon.
--- Sodo
(1641-1716)
A storm wind blows
out from among the grasses
the full moon grows.
--- Chora
(1729-1781)
The purpose of the program in Listing 21.4 is to show you how to invoke the
ANALYSIS
fread() and fwrite() functions in your program to perform block I/O operations. In Listing 21.4, the haiku.txt file is read by the fread() function, and then the fwrite() function is used to write the contents read from haiku.txt to another file called outhaiku.txt. You call the two C I/O functions from your own function, BlockReadWrite().
From the definition of the BlockReadWrite() function in lines 29–40, you can see that a character array called buff is defined with the number of elements of MAX_LEN + 1 in line 32, although you only read MAX_LEN number of characters by calling the fread() function in line 35. The reason is that you append a null character in line 36 after the last character read so that you ensure the block of characters saved in buff is treated as a string and can be printed out on the screen properly by the printf() function which is called in line 37.
The while loop, shown in lines 34–39, keeps calling the fread() function to read a character block with MAX_LEN elements, until the feof() function in line 34 returns 0, which means that the end of the text file has been reached. As shown in lines 35 and 38, you use the sizof operator to measure the size of the char data type because the elements in the buff array are all characters.
If everything goes smoothly, you should see the Japanese verses again on the screen or in
21
the outhaiku.txt file after running the program in Listing 21.4.
27 067231861x CH21 1/25/00 11:09 AM Page 370
370
Hour 21
Summary
In this lesson you learned the following important concepts and functions regarding disk file input and output in C:
• In C, a file can refer to a disk file, a terminal, a printer, or a tape drive.
• The data flow you transfer from your program to a file, or vice versa, is called a stream.
• A stream is a series of ordered bytes.
• Unlike a file, a stream is device independent.
• There are two stream formats: text stream and binary stream.
• The file position indicator in the FILE structure points to the position in a file where data will next be read from or written to.
• The fopen() function is used to open a file and associate a stream to the opened file.
• You can specify different modes for opening a file.
• The fclose() function is responsible for closing an opened file and disassociating a stream with the file.
• The fgetc() and fputc() functions read or write one character at a time.
• The fgets() and fputs() functions read or write one line at a time.
• The fread() and fwrite() functions read or write one block of data at a time.
• The feof() function can determine when the end of a file has been reached.
• In a binary file, the feof() function should be used to detect EOF.
In the next lesson you’ll learn more about disk file I/O in C.
Q&A
Q What are the differences between a text stream and a binary stream?
A
A text stream is a sequence of characters that may not have a one-to-one relationship with the data on the device. Text streams are normally used for textual data, which have a consistent appearance from one environment to another, or from one machine to another. For this reason, the data in a text file may be interpreted so that it appears correctly on the screen. A binary stream, on the other hand, is a sequence of bytes that has a one-to-one correspondence to those on the device.
Binary streams are primarily used for nontextual data that is needed to keep the exact contents on the device.
27 067231861x CH21 1/25/00 11:09 AM Page 371
Reading and Writing with Files
371
Q Why do you need a file pointer?
A
A file pointer is used to associate a stream with an opened file for reading or writing purpose. A pointer of the type FILE is called a file pointer. FILE is a typedef for a structure that contains overhead information about a disk file. A file pointer plays an important role in the communication between programs and disk files.
Q What does the
fclose()
function do before it closes an opened file?
A
As you know, all high-level I/O operations are buffered. One of the jobs of the fclose() function is to flush data left in the buffer to commit the I/O operations and ensure that no data is lost. For instance, when you finish writing several blocks of characters to an opened text file, you call the fclose() function to disassociate a specified stream and close the text file. The fclose() function will first flush all characters left in the buffer and write them into the text file before it closes the file.
In this way, all characters you write to the file will be saved properly.
Q What is the difference between
fgets()
and
gets()
?
A
The major difference between the fgets() and gets() functions is that the fgets() function includes a newline character in the array if the newline is encountered during the read operation, whereas the gets() function just replaces the newline character with a null character. Additionally, fgets() is much safer than gets() because it lets you specify the size of the array where the string is stored.
Workshop
To help solidify your understanding of this lesson, you are encouraged to answer the quiz questions and finish the exercises provided in the workshop before you move to the next lesson. The answers and hints to the questions and exercises are given in Appendix B,
“Answers to Quiz Questions and Exercises.”
Quiz
1. What do the following expressions do?
fopen(“test.bin”, “r+b”)
fopen(“test.txt” “a”)
fopen(“test.ini”, “w+”)
2. What’s wrong with the following code segment?
FILE *fptr;
21
int c;
if ((fptr = fopen(“test1.txt”, “r”)) == NULL){
while ((c=fgetc(fptr)) != EOF){
putchar(c);
}
}
fclose(fptr);
27 067231861x CH21 1/25/00 11:09 AM Page 372
372
Hour 21
3. What’s wrong with the following code segment?
FILE *fptr;
int c;
if ((fptr = fopen(“test2.txt”, “r”)) != NULL){
while ((c=fgetc(fptr)) != EOF){
fputc(c, fptr);
}
fclose(fptr);
}
4. What’s wrong with the following code segment?
FILE *fptr1, *fptr2;
int c;
if ((fptr1 = fopen(“test1.txt”, “r”)) != NULL){
while ((c=fgetc(fptr1)) != EOF){
putchar(c);
}
}
fclose(fptr1);
if ((fptr2 = fopen(“test2.txt”, “w”)) != NULL){
while ((c=fgetc(fptr1)) != EOF){
fputc(c, fptr2);
}
}
fclose(fptr2);
Exercises
1. Write a program to read the text file haiku.txt and count the number of characters in the file. Also, print out the content of the file and the total character number on the screen.
2. Write a program to receive a string entered by the user, and then save the string into a file with the name also given by the user.
3. Given the string “Disk file I/O is fun.” write a program to write the string into a file called test_21.txt by writing one character at a time. Meanwhile, print out the string on the screen.
4. Rewrite exercise 3. This time, try to write one block of characters (that is, one string) at a time.
28 067231861x CH22 4.10.2000 11:04 AM Page 373
HOUR 22
Using Special File
Functions
Disk space: the final frontier.
—Captain Kirk’s younger brother
In last hour’s lesson you learned the basics of reading and writing disk data files. In this lesson you’ll learn more about communication with disk data files. The main topics discussed in this hour are
• Random access to files
• Reading or writing binary data
• Redirecting the standard streams
In addition, the following C I/O functions are introduced in this lesson:
• The fseek(), ftell(), and rewind() functions
• The fscanf() and fprintf() functions
• The freopen() function
28 067231861x CH22 4.10.2000 11:04 AM Page 374
374
Hour 22
Random Access to Disk Files
So far you’ve learned how to read or write data sequentially to an opened disk file, known as
sequential access
. In other words, you start with the first byte and keep reading or writing each successive byte in order. In many cases, however, you need to access particular data somewhere in the middle of a disk file. One way to do it is to keep reading data from the file until the particular data is fetched. Obviously, this is not an efficient way, especially when the file contains many data items.
Random access
is another way to read or write data to disk files. In random access, specific file elements can be accessed in random order (that is, without reading through all the preceding data).
In C there are two I/O functions, fseek() and ftell(), that are designed to deal with random access.
The
fseek()
and
ftell()
Functions
As just mentioned, you need functions that enable you to access files randomly. The fseek() and ftell() functions provide you with such a capability.
In the previous lesson you learned that one of the members in the FILE structure is called the
file position indicator
. The file position indicator has to point to the desired position in a file before data can be read from or written to there. You can use the fseek() function to move the file position indicator to the spot you want to access in a file.
The syntax for the fseek() function is
AX
#include
int fseek(FILE *stream, long offset, int whence);
YNTS
Here stream is the file pointer associated with an opened file. offset indicates the num-
,
ber of bytes from a fixed position specified by whence that can have one of the following
,
integral values represented by SEEK_SET, SEEK_CUR, and SEEK_END. If it is successful, the fseek() function returns 0; otherwise, the function returns a nonzero value.
You can find the values represented by SEEK_SET, SEEK_CUR, and SEEK_END in the header file stdio.h.
If SEEK_SET is chosen as the third argument to the fseek() function, the offset is counted from the beginning of the file and the value of the offset should be greater than or equal to zero. If, however, SEEK_END is used, the offset starts from the end of the file, and the value of the offset should be negative. When SEEK_CUR is passed to the fseek() function, the offset is calculated from the current value of the file position indicator.