Chapter 11
Programming Problems Using File
11.1 Sorting a File of Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
11.2 Counting the Occurrences of Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
11.3 Counting the Occurrences of a Word . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
11.4 How to Comment Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
11.1 Sorting a File of Integers
This program reads integers from a file, sorts them, and stores the sorted integers into
another file. We have already learned how to read integers from a file in Section 10.2.2.
Chapter 9 explains how to sort arrays. Here we put these two things together. This is the
first few steps of the program:
1. Check whether there are command-line arguments for the input and the output file
names.
2. Open the input file.
3. Read integers from the file and count the number of integers in the file.
4. Allocate memory to store the integers.
As illustrated in Fig. 10.1, a file is a stream. Every time something is read from the file,
the stream moves forward. After counting the number of integers, the stream has reached
its end. To fill the array, it is necessary to read the file from the beginning again. We can
do this in several ways. One way is to close the file and open it again. The preferred way is
to use fseek. It goes to a particular position in a file. This is how to go to the beginning of
a file: fseek(fptr, 0, SEEK SET).
Some people believe that calling rewind is the same as fseek(fptr, 0, SEEK SET).
This is not true. The returned value of fseek reports whether it succeeds or not, but
rewind does not report either success or failure.
The remaining steps are:
5. Use fseek to go to the beginning of the file.
6. Read the file again and fill the array.
7. Sort the array.
8. Close the input file.
9. Open the output file.
10. Write the sorted array to the output file.
11. Close the output file.
12. Free the memory for the array.
The order of some of these steps may be changed. For example, the program may free the
array memory before or after closing the output file (steps 11 and 12). The orders of steps
8 and 9 can also be exchanged. However, the order of some steps cannot be changed. For
example, step 9 (opening the output file) must precede step 10 (writing to the output file).
This can only be determined by thinking logically about the code. When writing complex
programs, it is important to write down the steps before writing the code. This saves a lot
153
154 Intermediate C Programming
of debugging time, and often potential problems can be considered before typing a single
line of code.
Below is a sample solution for this program. If we compare the program and the steps
listed above, we will find a close correspondence between them. This program uses the
built-in qsort function to sort integers.
// sortint .c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
int compare func ( const void * arg1 , const void * arg2 )4
{5
const i n t * ptr1 = ( const i n t *) arg1 ; // cast type6
const i n t * ptr2 = ( const i n t *) arg2 ;7
const i n t val1 = * ptr1 ; // get the value from the address8
const i n t val2 = * ptr2 ;9
i f ( val1 < val2 ) // compare the value10
{ return -1; }11
i f ( val1 == val2 )12
{ return 0; }13
return 1;14
}15
int main ( i n t argc , char * argv [])16
{17
// need two file names : input and output18
i f ( argc < 3)19
{20
return EXIT _FAILUR E ;21
}22
// open the input file23
FILE * infptr ;24
infptr = fopen ( argv [1] , "r ");25
i f ( infptr == NULL )26
{27
return EXIT _FAILUR E ;28
}29
// count the number of i ntegers in the file30
int count = 0;31
int val ;32
while ( fscanf ( infptr , " %d" , & val ) == 1)33
{34
count ++;35
}36
// allocat e memory for the array37
int * arr ;38
arr = malloc ( s i z e o f ( i nt ) * count );39
i f ( arr == NULL )40
{41
fclose ( infptr ) ;42
return EXIT _FAILUR E ;43
}44
// go to the b e ginning of the file45
Programming Problems Using File 155
fseek ( infptr , 0 , SEEK_SET );46
// read the file again and fill the array47
int ind = 0; // array index48
while ( fscanf ( infptr , " %d" , & val ) == 1)49
{50
arr [ ind ] = val ;51
ind ++;52
}53
// sort the array54
qsort (& arr [0] , count , s i z e o f ( i n t ) , comp arefunc );55
// close the input file56
fclose ( infptr ) ;57
// open the output file58
FILE * outfptr ;59
outfptr = fopen ( argv [2] , "w ");60
i f ( outfptr == NULL )61
{62
free ( arr ); // do not forget to release memory63
return EXIT _FAILUR E ;64
}65
// write the sorted array to the output file66
for ( ind = 0; ind < count ; ind ++)67
{68
fprintf ( outfptr , "% dn ", arr [ ind ]) ;69
}70
// close outupt file71
fclose ( outfptr );72
// release the array s memory73
free ( arr );74
return EXIT _SUCCES S ;75
}76
11.2 Counting the Occurrences of Characters
The program reads characters from a file and counts their occurrences. The program
does not distinguish between uppercase characters and lowercase characters. Only the 26
Latin letters used in English are counted. If a character is not a Latin letter, the character
is ignored. The program then saves the occurrences into an output file.
The program has the following steps:
1. Check whether there are command-line arguments for the input and the output files.
2. Create an array of 26 integers. A fixed size array is preferred because the array’s size
is known in advance. This means that the array can be placed on the stack, and we
do not need to call malloc and free.
3. Open the input file.
4. Read the characters from the file. If the character is a Latin letter, increment the
corresponding array element.
5. Close the input file.
156 Intermediate C Programming
6. Open the output file.
7. Write the array’s elements to the output file.
8. Close the output file.
These steps are similar to the steps for the previous program, except the parts for
counting the characters. Below is a sample implementation of the above steps:
// coun tchar . c1
#in clude < stdio .h >2
#in clude < stdlib .h >3
#in clude < ctype .h >4
#d ef in e NUM_CHAR 265
int main ( i n t argc , char * argv [])6
{7
i f ( argc < 3) // need input and output8
{9
return EXIT _FAILUR E ;10
}11
// create an array of 26 integers12
char charcount [ N U M_CHAR ] = {0}; // i nitialize to zeros13
// without initialization , the elements are garbage14
// open the input file15
FILE * infptr ;16
infptr = fopen ( argv [1] , "r ");17
i f ( infptr == NULL )18
{19
return EXIT _FAILUR E ;20
}21
// count the occur rences of the characters22
int onechar ;23
do24
{25
onechar = fgetc ( infptr );26
i f ( isupper ( onechar ))27
{28
charcount [ o n e c h a r - A ] ++;29
}30
i f ( islower ( onechar ))31
{32
charcount [ o n e c h a r - a ] ++;33
}34
} while ( onechar != EOF ) ;35
// close the input file36
fclose ( infptr ) ;37
// open the output file38
FILE * outfptr ;39
outfptr = fopen ( argv [2] , "w ");40
i f ( outfptr == NULL )41
{42
return EXIT _FAILUR E ;43
}44
// write the array s e lements to the file45
Programming Problems Using File 157
int ind ;46
for ( ind = 0; ind < N UM_CHAR ; ind ++)47
{48
fprintf ( outfptr , "% c: %d n" , ind + A ,49
charcount [ ind ]);50
}51
// close outupt file52
fclose ( outfptr );53
return EXIT _SUCCES S ;54
}55
The main difference between this program and the previous program is in lines 24 to 35.
Line 27 uses the function isupper to determine whether the character is an uppercase
letter. This function is declared in ctype.h so the program needs to include this header
file. Calling isupper is equivalent to checking whether onechar is between ’A and Z’. The
ASCII value for A is 65 and the ASCII value for Z is 90. However, you should not check
whether onechar is between 65 and 90. There are a few reasons for this suggestion. First,
if you accidentally type 89 instead of 90, it is not easy to detect the mistake. It is difficult
remembering that Z is 90, not 89. By contrast, if you type Y instead Z’, it is easier to
detect the mistake. This brings us to the main reason for preferring A’ and ’Z to 64 and 90:
It is clear and easy to read. Clarity is one of the most important qualities of well-written
code. Did you notice that I incorrectly wrote 64, not 65? If you missed that mistake, it is
likely that you would miss similar mistakes in your programs.
How about converting uppercase letters to lowercase? Many students write
i f (( onechar >= 65) && ( onechar <= 90) )1
{2
onechar += 32;3
}4
This is bad. Why? It is difficult to understand the meaning of 65, 90, and 32. What
happens if we accidentally type 31 instead of 32? How much time does it take to find such
a mistake? It will take longer than you think. It is much better to write:
i f (( onechar >= A ) && ( onechar <= Z ))1
{2
onechar = ( onechar - A ) + a ;3
}4
Do not overlook the importance of these details. I have seen many students making
“small” mistakes like these. They are overly confident that they do not make mistakes.
When you write a complex program, the problems from these details can easily take hours
to detect and correct. Good programmers know this well, and dramatically improve their
efficiency by making things as simple and as clear as possible. It allows programmers to
write sophisticated computer programs more easily.
Lines 29 and 33 use the values in the ASCII table to calculate the corresponding index
for the array charcount. If the character is ’A’, then onechar - ’A’ is 0. If the character
is ’B’, then onechar - ’A’ is 1. If the character is ’c’, onechar - ’a’ is 2. Some students
write something like:
i f ( onechar == A )1
charcount [0] ++;2
i f ( onechar == B )3
charcount [1] ++;4
..................Content has been hidden....................

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