Chapter 10
Reading and Writing Files
10.1 Passing a File Name via argv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
10.2 Reading from Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
10.2.1 Reading Characters: fgetc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
10.2.2 Reading Integers: fscanf(... %d...) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
10.3 Writing to Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
10.4 Reading and Writing Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
We have already taken advantage of redirection to use files as inputs and outputs. This
chapter explains how to use C functions to read from or to write to files without using
redirection.
10.1 Passing a File Name via argv
A program has many ways to obtain input data, for example:
Using scanf to get data from a user through the keyboard.
Using scanf and redirection to get data from a file.
Using argc and argv to get data from the command line.
Using file operations to get data stored on a disk.
For the last option, the program must first obtain the file’s name. The file’s name is itself
a piece of data. The example below uses argv[1] as a file’s name. The program must check
whether argc is at least two to decide whether argv[1] can be used without generating a
memory error.
/*1
* chec k argc . c2
*/3
#in clude < stdio .h >4
#in clude < stdlib .h >5
int main ( i n t argc , char * argv [])6
{7
i f ( argc < 2)8
{9
printf (" Need to provide the file s name . n") ;10
return EXIT _FAILUR E ;11
}12
printf (" The name of the file is %s . n ", argv [1]) ;13
return EXIT _SUCCES S ;14
}15
141
142 Intermediate C Programming
Running the program without passing the file’s name on the command line will cause
an error message to be printed and the program returns EXIT FAILURE. Use gcc to compile
and link the program:
$ gcc -Wall -Wshadow file1.c -o file1
The program exits if running without any arguments:
$ ./file1
Need to provide the file’s name.
When the main function returns, the program terminates. By returning EXIT FAILURE, this
program informs the terminal that this program ends abnormally. If the file’s name is given,
then the program prints the file’s name:
$ ./file1 xyz
The name of the file is xyz.
10.2 Reading from Files
10.2.1 Reading Characters: fgetc
After getting the file’s name, we need to open the file for reading. This is accomplished by
calling the fopen function. The function requires two arguments. The first is the name of the
file, and the second specifies the “mode”. The mode determines how the file is opened—for
reading or for writing. They are two different ways of opening the same file. In this example,
we want to read the file, and this mode is specified by r in the second argument.
Calling fopen does not always open a file successfully. There are many reasons that can
make fopen fail. For example, the file may not exist, or the user running the program may
not have the permission to open the file. When fopen fails, it returns NULL. It is important
to check whether fopen returns NULL before attempting to read from or write to a file. After
opening a file, fgetc can be used to read the characters one by one.
// coun tchar . c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
int main ( i n t argc , char * argv [])4
{5
FILE * fptr ;6
int ch ;7
int counter = 0;8
i f ( argc < 2)9
{10
printf (" Need to provide the file s name . n") ;11
return EXIT _FAILUR E ;12
}13
fptr = fopen ( argv [1] , " r") ;14
i f ( fptr == NULL )15
{16
Reading and Writing Files 143
printf (" fopen fail . n") ;17
return EXIT _FAILUR E ;18
}19
printf (" The name of the file is %s . n ", argv [1]) ;20
do21
{22
ch = fgetc ( fptr );23
i f ( ch != EOF )24
{25
counter ++;26
}27
} while ( ch != EOF );28
fclose ( fptr );29
printf (" The file has %d cha racters . n" , counter );30
return EXIT _SUCCES S ;31
}32
/ * f i l e : (a)
E S S ; } EOF
* f i l e : (b)
E S S ; } EOF
fgetc '/'
f i l e : (c)
E S S ; } EOF
fgetc '*'
(d)
EOF
fgetc ' '
(e)
fgetc EOF
FIGURE 10.1: A file is a stream. This example uses the program source code as the input
file. (a) After calling fopen, the stream starts from the very first character of the file and
ends with EOF. EOF is a special character that does not actually exist in the file, but signifies
that there is no data left in the stream. (b),(c) Each time fgetc is called, one character is
taken out of the stream. (d) After calling fgetc enough times, all the characters in the file
are retrieved. We have not yet attempted to read past the end of the file. (e) Finally, the
end of file character EOF is returned because there are no more characters in the file.
How does fgetc work? After calling fopen, fptr points to a stream of characters, as
illustrated in Fig. 10.1. This stream starts at the beginning of the file. Every time fgetc
is called, one character is taken out from the stream. If the program keeps calling fgetc,
eventually all characters are taken and the special character EOF is returned.
This program counts the number of characters. A character may be a Latin character
(’a’ to ’z’ or ’A’ to ’Z’), a digit (’0’ to ’9’), a punctuation mark (such as ’,’ and ’;’), space, or
an invisible character. At the end of each line, a new line character ( n’) is also counted.
144 Intermediate C Programming
When the program attempts to read beyond the end of the file, fgetc returns EOF. This
character is called end of file and its symbol EOF is defined in stdio.h. If we search EOF
using Linux’ grep command:
$ grep EOF /usr/include/stdio.h
we should find
# define EOF (-1)
Its value is 1. The manual for fgetc says, fgetc() reads the next character from stream
and returns it as an unsigned char cast to an int, or EOF on end of file or error.
What does this mean? This function reads one character from a file. This character is
treated as an unsigned character because ASCII (American Standard Code for Information
Interchange) has only positive values. Unsigned characters can have values between 0 and
255 inclusive. The function then casts the character to an integer. Why is this necessary?
Because EOF is negative and is not an unsigned character. Thus fgetc returns 1, or 0
to 255 inclusive. This guarantees that EOF can be distinguished from the valid characters
that are actually in the file. Another way to detect the end of file is by calling the function
feof. This function returns a non-zero value if the end of file has been reached. Thus we
can replace line 21 by:
while (! feof ( fptr ) )21
and remove while ( ch != EOF ); at line 28.
This program reports the number of characters in the file. Suppose that the source for
this program is in the file file2.c and we compile and execute it like so:
$ gcc -Wall -Wshadow file2.c -o file2
$ ./file2 file2.c
The name of the file is file2.c.
The file has 656 characters.
Linux has a program called wc and it reports the numbers of lines, words, and characters
in a file. The program reports that the file2.c has 32 lines, 96 words, and 656 characters.
The wc program considers a word to be a non-zero-length sequence of characters delimited
by space.
$ wc file2.c
32 96 656 file2.c
Operating systems usually restrict the number files that a program can open at once
to ensure that one program does not use too many resources. Thus programs should call
fclose when a previously opened file is no longer needed. Just as with malloc and free,
it is a good habit to type fclose right after typing fopen and then insert appropriate
code between them. This can prevent forgetting to call fclose. In fact, fopen will allocate
memory in the program. Thus, if a program does not call fclose, then the program has
memory leak. Some students write this:
i f ( fptr == NULL )15
{16
printf (" fopen fail . n") ;17
fclose ( fptr );18
}19
Reading and Writing Files 145
This is wrong. If fptr is NULL, then fopen fails to open the file. If the file is not open,
then it cannot be closed. The documentation of fclose clearly says:
The behaviour of fclose() is undefined if the stream parameter is an
illegal pointer, or is a descriptor already passed to a previous
invocation of fclose().
Thus, fclose(NULL) is bad, since it results in unpredictable behavior. Also, note that
it is an error to close the same file pointer twice.
What is stored at the heap memory pointed by fptr? The following uses gdb to show
the contents at the memory address pointed by fptr.
(gdb) print * fptr
$2 = {_flags = -72539000, _IO_read_ptr = 0x0, _IO_read_end = 0x0,
_IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x0,
_IO_write_end = 0x0, _IO_buf_base = 0x0, _IO_buf_end = 0x0,
_IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0,
_markers = 0x0, _chain = 0x7ffff7dd4180, _fileno = 3, _flags2 = 0,
_old_offset = 0, _cur_column = 0, _vtable_offset = 0 ’00’,
_shortbuf = "", _lock = 0x6020f0, _offset = -1, __pad1 = 0x0, __pad2
= 0x602100, __pad3 = 0x0, __pad4 = 0x0, __pad5 = 0, _mode = 0,
_unused2 = ’00’ <repeats 19 times>}
As we can see, the data that the FILE * points to is complicated. Fortunately, we do
not need to know the details since they are purely internal to the C library, and should not
be modified or examined directly.
10.2.2 Reading Integers: fscanf(... %d...)
In addition to fgetc, C provides many functions for reading data from a file. One of
them is fscanf. It is very similar to scanf, except that it requires one more argument. The
first argument is a FILE pointer. The following program adds the numbers in a file.
// fscanf . c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
int main ( i n t argc , char * argv [])4
{5
FILE * fptr ;6
int val ;7
int sum = 0;8
i f ( argc < 2)9
{10
printf (" Need to provide the file s name . n") ;11
return EXIT _FAILUR E ;12
}13
fptr = fopen ( argv [1] , " r") ;14
i f ( fptr == NULL )15
{16
printf (" fopen fail . n") ;17
return EXIT _FAILUR E ;18
}19
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset