So far, our programs have read data from the standard input (the keyboard) and sent output to the standard output (the screen). We have just seen how to read data from a file. We now show you how you can send output to a file.
The data for each employee consists of a first name, a last name, the number of hours worked, and the rate of pay. The data will be stored in a file paydata.txt and output will be sent to the file payroll.txt.
In order to show you another way to read a string, we will assume that the data is stored in the file as follows:
Maggie May 50 12.00 Akira Kanda 40 15.00 Richard Singh 48 20.00 Jamie Barath 30 18.00 END
We use the “first name” END as the end-of-data marker.
Regular pay, overtime pay, and net pay will be calculated as described in Section 4.4.1. The employee name, hours worked, rate of pay, regular pay, overtime pay, and net pay are printed under a suitable heading. In addition, we will write the program to do the following:
• Count how many employees are processed.
• Calculate the total wage bill (total net pay for all employees).
• Determine which employee earned the highest pay and how much. We will ignore the possibility of a tie.
For the sample data, the output should look like this:
Name Hours Rate Regular Overtime Net Maggie May 50.0 12.00 480.00 180.00 660.00 Akira Kanda 40.0 15.00 600.00 0.00 600.00 Richard Singh 48.0 20.00 800.00 240.00 1040.00 Jamie Barath 30.0 18.00 540.00 0.00 540.00 Number of employees: 4
Total wage bill: $2840.00
Richard Singh earned the most pay of $1040.00
An outline of the algorithm for reading the data is as follows:
read firstName
while firstName is not "END" do read lastName, hours, rate do the calculations
print results for this employee read firstName
endwhile
We will use the specification %s in fscanf for reading the names. Suppose we have declared firstName as
char firstName[20];
We can read a string into firstName with the statement fscanf(in, "%s", firstName);
The specification %s must be matched with a character array, like firstName. As mentioned in Section 3.4, when an array name is an argument to scanf (or fscanf), we must not write &
before it.
%s is used for reading a string of characters not containing any whitespace characters.
Beginning with the next non-whitespace character, characters are stored in firstName until the next whitespace character is encountered. It is up to us to make sure that the array is big enough to hold the string.
Because a whitespace character ends the reading of a string, %s cannot be used to read a string containing blanks. For this reason, we will use separate variables for first name (firstName) and last name (lastName).
For example, suppose the next piece of data contains (◊ denotes a space):
◊◊◊Robin◊◊◊◊Hood◊◊
The statement
fscanf(in, "%s", firstName);
will skip over spaces until it reaches the first non-whitespace character R. Starting with R, it stores characters in firstName until it reaches the next space, the one after n. Reading stops and Robin is stored in firstName. The data pointer is positioned at the space after n. If we now execute
fscanf(in, "%s", lastName);
fscanf will skip over spaces until it reaches H. Starting with H, it stores characters in lastName until it reaches the space after d. Reading stops and Hood is stored in lastName. If d were the last character on the line, the end-of-line character (which is whitespace) would have stopped the reading.
Because of the way %s works, we will need to read the first and last names separately.
However, in order to get the output to line up neatly as shown on the previous page, it would be more convenient to have the entire name stored in one variable (name, say). Suppose Robin is stored in firstName and Hood is stored in lastName. We will copy firstName to name with strcpy(name, firstName);
We will then add a space with strcat(name, " ");
strcat is a predefined string function that allows us to join (concatenate) two strings. It stands for “string concatenation”. If s1 and s2 are strings, strcat(s1, s2) will add s2 to the end of s1. It assumes that s1 is big enough to hold the joined strings.
We will then add lastName with strcat(name, lastName);
Using our example, at the end of all this, name will contain Robin Hood.
In our program, we will use the specification %-15s to print name. This will print name left justified in a field width of 15. In other words, all names will be printed using 15 print columns.
This is necessary for the output to line up neatly. To cater for longer names, you can increase the field width.
To use the string functions, we must write the directive
#include <string.h>
at the head of our program if we want to use the string functions supplied by C.
Our program will need to check if the value in firstName is the string "END". Ideally, we would like to say something like
while (firstName != "END") { //cannot write this in C
but we cannot do so since C does not allow us to compare strings using the relational operators.
What we can do is use the predefined string function strcmp (string compare).
If s1 and s2 are strings, the expression strcmp(s1, s2) returns the following values:
• 0 if s1 is identical to s2
• < 0 if s1 is less than s2 (in alphabetical order)
• > 0 if s1 is greater than s2 (in alphabetical order) For example,
strcmp("hello", "hi") is < 0 strcmp("hi","hello") is > 0 strcmp("allo","allo") is 0
Using strcmp, we can write the while condition as while (strcmp(firstName, "END") != 0)
If strcmp(firstName, "END") is not 0, it means that firstName does not contain the word END so we have not reached the end of the data; the while loop is entered to process that employee.
When faced with a program that requires so many things to be done, it is best to start by working on part of the problem, getting it right, and then tackling the other parts. For this problem, we can start by getting the program to read and process the data without counting, finding the total or finding the highest-paid employee.
Program P5.8 is based on program P4.7 (Section 4.6.2).
Program P5.8
#include <stdio.h>
#include <string.h>
#define MaxRegularHours 40
#define OvertimeFactor 1.5 int main() {
FILE * in = fopen("paydata.txt", "r");
FILE * out = fopen("payroll.txt", "w");
char firstName[20], lastName[20], name[40];
double hours, rate, regPay, ovtPay, netPay;
fprintf(out,"Name Hours Rate Regular Overtime Net\n\n");
fscanf(in, "%s", firstName);
while (strcmp(firstName, "END") != 0) {
fscanf(in, "%s %lf %lf", lastName, &hours, &rate);
if (hours <= MaxRegularHours) { regPay = hours * rate;
ovtPay = 0;
} else {
regPay = MaxRegularHours * rate;
ovtPay = (hours - MaxRegularHours) * rate * OvertimeFactor;
}
netPay = regPay + ovtPay;
//make one name out of firstName and lastName
strcpy(name,firstName); strcat(name," "); strcat(name,lastName);
fprintf(out, "%-15s %5.1f %6.2f", name, hours, rate);
fprintf(out, "%9.2f %9.2f %7.2f\n", regPay, ovtPay, netPay);
fscanf(in, "%s", firstName);
}
fclose(in);
fclose(out);
}
Comments on Program P5.8
• We use the “file pointers” in and out for reading data from paydata.txt and sending output to payroll.txt.
• Since data is being read from a file, prompts are not required.
• We use fscanf for reading data and fprintf for writing output.
• We use fclose to close the files.
• We print a heading with the following statement:
fprintf(out,"Name Hours Rate Regular Overtime Net\n\n");
• To get the output to line up nicely, you will need to fiddle with the spaces between the words and the field widths in the statements that print the results. For example, there are 12 spaces between e and H, 3 spaces between s and R, 2 between e and R, 2 between r and O, and 5 between e and N.
• You should experiment with the field widths in the fprintf statements (which write one line of output) to see what effect it has on your output.
• We use a while loop to process several employees. When the “first name”
END is read, the program knows it has reached the end of the data. It closes the files and stops.
Now that we’ve got the basic processing right, we can add the statements to perform the other tasks. Program P5.9 is the complete program that counts the employees, calculates the total wage bill, and determines the employee who earned the highest salary.
Counting the employees and finding the total wage bill are fairly straightforward. We use the variables numEmp and wageBill, which are initialized to 0 before the loop. They are incremented inside the loop and their final values are printed after the loop. If you have difficulty following the code, you need to reread Sections 5.1 and 5.2. We use numEmp++ to add 1 to numEmp and wageBill += netPay to add netPay to wageBill.
The variable mostPay holds the most pay earned by any employee. It is initialized to 0. Each time we calculate netPay for the current employee, we compare it with mostPay. If it is bigger, we set mostPay to the new amount and save the name of the employee (name) in bestPaid.
Program P5.9
#include <stdio.h>
#include <string.h>
#define MaxRegularHours 40
#define OvertimeFactor 1.5 int main() {
FILE * in = fopen("paydata.txt", "r");
FILE * out = fopen("payroll.txt", "w");
char firstName[20], lastName[20], name[40], bestPaid[40];
double hours, rate, regPay, ovtPay, netPay;
double wageBill = 0, mostPay = 0;
int numEmp = 0;
fprintf(out,"Name Hours Rate Regular Overtime Net\n\n");
fscanf(in, "%s", firstName);
while (strcmp(firstName, "END") != 0) { numEmp++;
fscanf(in, "%s %lf %lf", lastName, &hours, &rate);
if (hours <= MaxRegularHours) { regPay = hours * rate;
ovtPay = 0;
}
else {
regPay = MaxRegularHours * rate;
ovtPay = (hours - MaxRegularHours) * rate * OvertimeFactor;
}
netPay = regPay + ovtPay;
//make one name out of firstName and lastName
strcpy(name,firstName); strcat(name," "); strcat(name,lastName);
fprintf(out, "%-15s %5.1f %6.2f", name, hours, rate);
fprintf(out, "%9.2f %9.2f %7.2f\n", regPay, ovtPay, netPay);
if (netPay > mostPay) { mostPay = netPay;
strcpy(bestPaid, name);
}
wageBill += netPay;
fscanf(in, "%s", firstName);
} //end while
fprintf(out, "\nNumber of employees: %d\n", numEmp);
fprintf(out, "Total wage bill: $%3.2f\n", wageBill);
fprintf(out,"%s earned the most pay of $%3.2f\n",bestPaid, mostPay);
fclose(in); fclose(out);
}