Read Sams Teach Yourself C in 24 Hours Online
Authors: Tony. Zhang
22: CharReadWrite(fptr2, fptr1);
23: fclose(fptr1);
24: fclose(fptr2);
25: }
26:
27: return reval;
28: }
29: /* function definition */
30: void CharReadWrite(FILE *fin, FILE *fout)
31: {
32: int c;
33:
34: while ((c=fgetc(fin)) != EOF){
35: fputc(c, fout); /* write to a file */
36: putchar(c); /* put the character to the screen */
37: }
38: }
After running the executable, 21L02.exe, on my machine, I get the following output: 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.2 is to read one character from a file,
ANALYSIS
write the character to another file, and then display the character on the screen.
Before running the program, you need to create a text file called haiku.txt with the contents shown above, and save it in the same directory as the executable program.
In Listing 21.2 there is a function called CharReadWrite(), which has two file pointers as its arguments. (See the declaration of the CharReadWrite() function in line 6.) The statement in line 10 defines two file pointers, fptr1 and fptr2, which are used later in the program. Lines 11 and 12 define two character arrays, filename1 and filename2, and initialize the two arrays with two strings containing filenames, outhaiku.txt and haiku.txt.
27 067231861x CH21 1/25/00 11:09 AM Page 363
Reading and Writing with Files
363
In line 15, a text file with the name outhaiku.txt is opened for writing. outhaiku.txt is contained by the filename1 array. The file pointer fptr1 is associated with the file. If the fopen() function returns NULL, which means an error has occurred, a warning message is printed out in line 16. Also, in line 17, the reval variable is assigned 1 and is represented by the enum name FAIL.
If the file outhaiku.txt is opened successfully, another text file, called haiku.txt, is opened for reading in line 18. The file pointer fptr2 is associated with the opened text file.
If no error occurs, the CharReadWrite() function is called in line 22 with two file pointers, fptr1 and fptr2, passed to the function as arguments. From the definition of the CharReadWrite() function in lines 30 and 38, you see that there is a while loop that keeps calling the fgetc() function to read the next character from the haiku.txt text file until the function reaches the end of the file (see line 34).
Within the while loop, the fputc() function in line 35 writes each character read from the haiku.txt file to another text file, outhaiku.txt, which is pointed to by fout. In addition, putchar() is called in line 36 in order to put the character returned by the fgetc() function on the screen.
After the CharReadWrite() function finishes its job, the two opened files, which are associated with fptr1 and fptr2, are each closed with a call to the fclose() function respectively in lines 23 and 24.
As mentioned earlier, the haiku.txt file contains two pieces of Japanese haiku written by Sodo and Chora. If the program in Listing 21.2 is run successfully, you will see the two pieces of haiku shown on the screen, and they are written into the outhaiku.txt file as well. You can view outhaiku.txt in a text editor to confirm that the content of haiku.txt has been correctly copied to outhaiku.txt.
One Line at a Time
Besides reading or writing one character at a time, you can also read or write one character line at time. There is a pair of C I/O functions, fgets() and fputs(), that allows you to do so.
The syntax for the fgets() function is
AX
#include
char *fgets(char *s, int n, FILE *stream);
21
YNTS
Here s references a character array that is used to store characters read from the opened
,
file pointed to by the file pointer stream. n specifies the maximum number of array ele-
,
ments. If it is successful, the fgets() function returns the char pointer s. If EOF is 27 067231861x CH21 1/25/00 11:09 AM Page 364
364
Hour 21
,
encountered, the fgets() function returns a null pointer and leaves the array untouched.
,
If an error occurs, the function returns a null pointer, and the contents of the array are unknown.
The fgets() function can read up to n-1 characters, and can append a null character after the last character fetched, until a newline or an EOF is encountered. Note that if a newline is encountered during the read operation, the fgets() function includes the newline in the array. This is different from what the gets() function does. The gets() function just replaces the newline character with a null character. (The gets() function was introduced in Hour 13, “Manipulating Strings.”)
The syntax for the fputs() function is
AX
#include
int fputs(const char *s, FILE *stream);
YNTS
Here s points to the array that contains the characters to be written to a file associated
,
with the file pointer stream. The const modifier indicates that the content of the array pointed to by s cannot be changed by the fputs() function. (You learned about the const
,
modifier in Hour 14, “ Understanding Scope and Storage Classes.”) If it fails, the fputs() function returns a nonzero value; otherwise, it returns zero.
Note that the character array must include a null character at the end of the string as the terminator to the fputs() function. Also, unlike the puts() function, the fputs() function does not insert a newline character to the string written to the file. (The puts() function was introduced in Hour 13, “Manipulating Strings.”)
You can modify the program in Listing 21.2 to read or write one character line at a time by calling the fgets() and fputs() functions. The modified version is shown in Listing 21.3.
TYPE
LISTING 21.3
Reading and Writing One Character Line at a Time
1: /* 21L03.c: Reading and writing one line at a time */
2: #include
3:
4: enum {SUCCESS, FAIL, MAX_LEN = 81};
5:
6: void LineReadWrite(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:
27 067231861x CH21 1/25/00 11:09 AM Page 365
Reading and Writing with Files
365
15: if ((fptr1 = fopen(filename1, “w”)) == NULL){
16: printf(“Cannot open %s for writing.\n”, filename1);
17: reval = FAIL;
18: } else if ((fptr2 = fopen(filename2, “r”)) == NULL){
19: printf(“Cannot open %s for reading.\n”, filename2);
20: reval = FAIL;
21: } else {
22: LineReadWrite(fptr2, fptr1);
23: fclose(fptr1);
24: fclose(fptr2);
25: }
26:
27: return reval;
28: }
29: /* function definition */
30: void LineReadWrite(FILE *fin, FILE *fout)
31: {
32: char buff[MAX_LEN];
33:
34: while (fgets(buff, MAX_LEN, fin) != NULL){
35: fputs(buff, fout);
36: printf(“%s”, buff);
37: }
38: }
Because the program in Listing 21.3 reads the same text file, haiku.txt, as the program in Listing 21.2 did, I get the same output on the screen:
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)
From the program in Listing 21.3, you can see that a function called
ANALYSIS
LineReadWrite() has replaced the CharReadWrite() function.
The definition of the LineReadWrite() function is shown in lines 30–38. The fgets() function is called repeatedly in a while loop to read one character line at a time from the
21
haiku.txt text file, until it reaches the end of the text file. In line 34, the array name buff and the maximum number of the array elements MAX_LEN are passed to the fgets() function, along with the file pointer fin that is associated with the opened haiku.txt file.
27 067231861x CH21 1/25/00 11:09 AM Page 366
366
Hour 21
Meanwhile, each line read by the fgets() function is written to another opened text file called outhaiku.txt that is associated with the file pointer fout. This is done by calling the fputs() function in line 35.
The statement in line 36 prints the contents of each string on the screen so that you see the two pieces of Japanese verses after running the program in Listing 21.3. Also, you can view the outhaiku.txt file in a text editor to make sure that the content of the haiku.txt file has been copied to the outhaiku.txt file.
In the previous hour, we used the gets() function to read data input from the keyboard. Since gets() does not know the size of the character array you pass to it, it simply reads data until a newline is encountered. This is actually quite dangerous, since the user could very easily type more characters than your array will hold. As a result, your other variables would get overwritten and your program will crash or, at best, behave unpredictably.
Fortunately, fgets() provides a much safer way of reading input from the keyboard, if you pass stdin as the stream from which to read.
The following code uses fgets() to read input from stdin into a string str.
int length = 80;
char str[80];
fgets(str, length, stdin);
if (str[(length = strlen(str) – 1)] == ‘\n’)
str[length] = ‘\0’; /* replace the ‘\n’ */
else
while (getchar() != ‘\n’)
; /* discard input until the newline */
As you can see, this method involves a little more work than gets() because we have to get rid of the newline character which fgets() stores in our array. However, it is well worth it because this is a much safer, cleaner way of dealing with user input than using gets().
One Block at a Time
If you like, you can also read or write a block of data at a time. In C, there are two I/O
functions, fread() and fwrite(), that can be used to perform block I/O operations. The fread() and fwrite() functions are mirror images of each other.
The syntax for the fread() function is
AX
#include
size_t fread(void *ptr, size_t size, size_t n, FILE *stream);
YNTS
Here ptr is a pointer to an array in which the data is stored. size indicates the size of
,
each array element. n specifies the number of elements to read. stream is a file pointer 27 067231861x CH21 1/25/00 11:09 AM Page 367
Reading and Writing with Files
367
,
that is associated with the opened file for reading. size_t is an integral type defined in the header file stdio.h. The fread() function returns the number of elements actually read.
,
The number of elements read by the fread() function should be equal to the value specified by the third argument to the function, unless an error occurs or an EOF (end-of-file) is encountered. The fread() function returns the number of elements that are actually read during the attempt, if an error occurs or an EOF is encountered.
The syntax for the fwrite() function is
AX
#include
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);
YNTS
Here ptr references the array that contains the data to be written to an opened file
,
pointed to by the file pointer stream. size indicates the size of each element in the array.
n
,
specifies the number of elements to be written. The fwrite() function returns the number of elements actually written.
If no error has occurred, the value returned by fwrite() should equal the third argument in the function. The return value may be less than the specified value if an error occurs.
Note that it’s the programmer’s responsibility to ensure that the array is large enough to hold data for either the fread() function or the fwrite() function.
In C, a function called feof() can be used to determine when the end of a file is encountered. This function is more useful when you’re reading a binary file because the values of some bytes may be equal to the value of EOF. That is to say, the character which is used as an end-of-file marker in a text file can easily occur in a binary file, but in that case it is not intended to mark end of the file. If you try to determine the end of a binary file by checking the value returned by fread(), you may end up at a wrong position.
Using the feof() function helps you to avoid mistakes in determining the end of a file, because it checks whether the file position indicator has actually reached the end of the file, regardless of any EOF character present.
The syntax for the feof() function is
AX
#include
int feof(FILE *stream);
YNTS
Here stream is the file pointer that is associated with an opened file. The feof() func-
,
,
tion returns 0 if the end of the file has not been reached; otherwise, it returns a nonzero
21
integer.
27 067231861x CH21 1/25/00 11:09 AM Page 368
368
Hour 21
The program in Listing 21.4 demonstrates how to read and write one block of characters at a time by calling the fread() and fwrite() functions. In fact, the program in Listing 21.4 is another modified version of the program from Listing 21.2.