146 Intermediate C Programming
printf (" The name of the file is %s . n ", argv [1]) ;20
while ( fscanf ( fptr , " %d" , & val ) == 1)21
{22
printf (" %d ", val ) ;23
sum += val ;24
}25
fclose ( fptr );26
printf (" nThe sum is %d .n " , sum );27
return EXIT _SUCCES S ;28
}29
This program keeps reading until no more integers can be read. Each call of fscanf
function returns the number of value(s) successfully read. We can use fscanf to attempt to
read multiple values at once. This example reads only one integer at a time. The returned
value will be either 1 if a single value is successfully read, or 0 if no value can be successfully
read. This means to keep reading as long as fscanf can still find another integer in the
file. A common mistake is thinking that fscanf returns the value read from this file. This
is wrong; instead, fscanf returns how many values are read from the file. The pattern
"%d" indicates that we only attempt to read one integer. If only one integer is read, fscanf
returns 1, regardless of the integer’s value. Every time fscanf is called, the file stream
moves forward and eventually reaches the end of the file.
Suppose we have a file called intfile that stores some integers:
4 7 8
32
71
6 -2 5 8
Below is the output when we run the program with intfile as the command-line argument:
The name of the file is intfile.
4 7 8 32 71 6 -2 5 8
The sum is 139.
Compared with fgetc, fscanf has several advantages:
When using %d, fscanf skips characters (such as space and new line n’) that are
not digits.
If two integers are separated by characters that are not digits, fscanf separates the
two integers automatically.
When fgetc reads the first character, it is not the integer 4, but instead the character
4 because it is treated as a character. A character can be converted to an integer
using the ASCII table. Type
$ man ascii
into the terminal to see an ASCII table, and note that the character ’4 has the decimal
value 52.
If a number is greater than 9, the number has two or more digits. Using fgetc, only
one digit is read at a time. If the number is 123, then we need to call fgetc three times
in order to get the three digits. Moreover, we need to change the three characters 1’,
2’, and ’3 (ASCII values 49, 50, 51) to the integer value 123 (one hundred and twenty
three). This is done by fscanf automatically.
Due to the above reasons, if a program reads integers from a file, fscanf is a better
choice than fgetc.
Reading and Writing Files 147
10.3 Writing to Files
We can use fprintf to write information to a file. It is very similar to printf; the
difference is that fprintf writes information to a file and printf writes information to the
computer screen. The following example shows how to write a program that reads integers
from two input files, adds the values, and stores the sum (another integer) into the output
file, one integer per line. This program takes three command-line arguments:
argv[1]: Name of the first input file.
argv[2]: Name of the second input file.
argv[3]: Name of the output file.
Each input file contains some integers. It is possible that several integers are in the same
line separated by space. It is also possible that the two files contain different numbers of
integers. If this happens, after running out of the integers from the shorter file, the program
copies the remaining integers from the longer input file to the output file. The program
does not know how many integers are stored in either file. The program ignores space in
each line and it also ignores empty lines. For simplicity, the program does not not consider
overflow or underflow of integers.
These are the two sample input files:
6255 70771
69652
3474 3003
4334 53864
7380 66105
15816
7
99558
8813 2379
10
7484 350211
4864 47841
8816 81132
3895 86773
70264
5
1937 12826
86387
8
9561 2391 5681 84529
498 9070 4930 877510
670 521 358211
12
864413
The output file should be:
111191
118612
157813
115874
41955
130116
124127
93178
78929
1021910
1951611
1120412
591813
1593614
400015
148 Intermediate C Programming
907016
493017
877518
67019
52120
358221
864422
The following program solves this problem:
// addint . c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
int main ( i n t argc , char * argv [])4
{5
i f ( argc < 4) // need two inputs and one output6
{7
return EXIT _FAILUR E ;8
}9
FILE * fin1 ;10
FILE * fin2 ;11
// open the two input files12
fin1 = fopen ( argv [1] , " r") ;13
i f ( fin1 == NULL ) // fail to open14
{15
return EXIT _FAILUR E ;16
}17
fin2 = fopen ( argv [2] , " r") ;18
i f ( fin2 == NULL )19
{20
fclose ( fin1 ) ; // need to close opened file21
return EXIT _FAILUR E ;22
}23
// open the output file24
FILE * fout ;25
fout = fopen ( argv [3] , " w") ;26
i f ( fout == NULL )27
{28
fclose ( fin1 ) ;29
fclose ( fin2 ) ;30
return EXIT _FAILUR E ;31
}32
33
int val1 ;34
int val2 ;35
int in1ok = 1; // can still read input file 136
int in2ok = 1; // can still read input file 237
// continu e as long as one file still has numbers38
while (( in1ok == 1) || ( in2ok == 1) )39
{40
val1 = 0; // reset the values before reading from files41
val2 = 0;42
Reading and Writing Files 149
i f ( fscanf ( fin1 , " %d " , & val1 ) != 1) // do not use == 043
{44
in1ok = 0; // cannot read input file 1 any more45
}46
i f ( fscanf ( fin2 , " %d " , & val2 ) != 1)47
{48
in2ok = 0; // cannot read input file 1 any more49
}50
i f (( in1ok == 1) || ( in2ok == 1) )51
{52
fprintf ( fout , " %d n" , val1 + val2 ); // save the sum53
}54
}55
/* close the files */56
fclose ( fin1 );57
fclose ( fin2 );58
fclose ( fout );59
60
return EXIT _SUCCES S ;61
}62
Line 6 checks whether enough arguments have been provided. Lines 13 to 32 open the
files. If fopen fails, then the program returns EXIT FAILURE. Please remember to close all
successfully opened files; otherwise, the program leaks memory allocated by fopen. At line
21, the program has failed to open the second file, and thus needs to close the first opened
file before returning. The condition at line 39 means “continue if one (or both) of the files
still has numbers”. This handles the situation when the two files have different numbers of
integers. The variables in1ok and in2ok are updated at lines 45 and 49.
Note that when a file reaches its end, fscanf returns EOF, and not zero. A common
mistake at lines 43 and 47 is using == 0. Since EOF is 1, if we replace != 1 by == 0 at
lines 43 and 47 then the program will enter an infinite loop. If the program reads successfully
from at least one of the two files, the program writes the sum to the output file. Lines 41 and
42 reset the values to zero. This is necessary because one file may have already reached the
end, in which case calling fscanf will not update one of val1 and val2. Without resetting
the values we get the wrong answer when one file is longer than the other.
This program specifically does not consider overflowing or underflowing of integers. What
does this mean? When a program creates an integer variable, the size of the variable is fixed
(dependent on the machine). Suppose an integer has 4 bytes, i.e., sizeof(int) is 4. One
byte is 8 bits and each bit can hold either 0 or 1. Thus, a 4-byte integer can hold 32 bits,
namely 2
32
possible values. The possible values include both positive and negative integers.
An integer can hold a value between 2
31
1 (2147483647) and 2
31
(2147483648), totally
2
32
possible values. If a file contains a number greater than 2147483647 or smaller than
2147483648, fscanf will not work. Thus the behavior of the program is unspecified if the
input numbers are too large or too small. By stating this, we put the burden on the user to
ensure that the numbers are within the range.
150 Intermediate C Programming
10.4 Reading and Writing Strings
Earlier sections showed how to read characters and integers by using fgetc and fscanf.
How can a program read a string, for example, someone’s name? There are several solutions.
One solution uses fgetc reading one character at a time. Another solution uses fscanf
with %s.
// fsca nfstr . c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
#d ef in e MAXSIZE 64
int main ( i n t argc , char * argv [])5
{6
FILE * fptr ;7
i f ( argc < 2)8
{9
printf (" Need to provide the file s name . n") ;10
return EXIT _FAILUR E ;11
}12
fptr = fopen ( argv [1] , " r") ;13
i f ( fptr == NULL )14
{15
printf (" fopen fail . n") ;16
return EXIT _FAILUR E ;17
}18
char buffer [ MAXSIZE ];19
while ( fscanf ( fptr , " %5 s ", buffer ) == 1)20
{21
printf (" %s n" , buffer ) ;22
}23
fclose ( fptr );24
return EXIT _SUCCES S ;25
}26
Line 20 reads one word at a time by using %s in fscanf. The function distinguishes
words by looking for spaces and new line characters (’n’). Adding a number between %
and s tells fscanf to limit the number of characters in a word. For example fscanf(fptr,
"%5s", buffer) limits the length of the word to 5 characters. Please remember a word is
a string and it ends with 0’. Thus, the length of the buffer (line 19) must be at least one
larger than the number between % and s to accommodate ’0’. If we do not put any number
between % and s and a word in the file is long, then the program will use up the memory
space in buffer, and write past the end. When this occurs, the program’s behavior is
undefined due to invalid memory accesses. When writing programs, it is important to make
sure that the programs cannot have invalid memory accesses regardless of the input data.
Even a malicious user should not be able to cause an invalid memory access. If the input
is not checked carefully, a malicious user may, for example, enter 20,000 characters for a
person’s name. This is called the “buffer overflow attack” and is one of the most common
security attacks.
In addition to fgetc and fscanf, fgets is another function for reading data from a file.
This function takes three arguments:
..................Content has been hidden....................

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