/* * Program to read file of grades and summarize. * * Name of file is from command-line argument. * * File consists of one or more lines containing an assignment name * (text, no spaces) and two scores (floating-point numbers), a "your * score" and a "perfect score". * * Output is a formatted list of input, sorted by assignment, plus an * average (computed as sum of your scores divided by sum of perfect scores). */ #include #include #include #include /* somewhat arbitrary sizes of things */ #define LINESIZE 200 #define MAX_ASSIGNMENTS 100 #define NAMESIZE 20 #define FMT_NAME "%20s" /* for reading assignment name */ #define PFMT_NAME "%-20s" /* for printing assignment name */ /* struct for individual grade */ typedef struct { char name[NAMESIZE+1]; double score; double perfect_score; } assignment_t; /* parse one line, returning true if okay or false if invalid */ bool parse_line(char *line, assignment_t *a); /* compare for qsort */ int compare_assignments(const void *e1, const void *e2); /* main program */ int main(int argc, char *argv[]) { if (argc != 2) { printf("usage: %s infile\n", argv[0]); return 1; } FILE * infile = fopen(argv[1], "r"); if (infile == NULL) { printf("cannot open input file %s\n", argv[1]); return 1; } assignment_t assignments[MAX_ASSIGNMENTS]; int num_assignments = 0; char line[LINESIZE+1]; /* * read in lines of input to EOF or error, putting each line into * an element of "assignments" */ while (fgets(line, sizeof line, infile) != NULL) { if (strchr(line, '\n') == NULL) { printf("line %d too long (max length %d) \n", num_assignments + 1, LINESIZE); fclose(infile); return 1; } if (num_assignments == MAX_ASSIGNMENTS) { printf("too many assignments (max %d)\n", MAX_ASSIGNMENTS); fclose(infile); return 1; } if (!parse_line(line, &assignments[num_assignments])) { printf("invalid line %d\n", num_assignments+1); fclose(infile); return 1; } num_assignments += 1; } fclose(infile); /* sort array of assignments by "name" field */ qsort(assignments, num_assignments, sizeof(assignments[0]), compare_assignments); /* * go through list of assignments, printing "nicely" and keeping track * of point totals. */ double total_points = 0.0; double total_perfect_points = 0.0; for (int i = 0; i < num_assignments; ++i) { printf(PFMT_NAME"%10.2f %10.2f\n", assignments[i].name, assignments[i].score, assignments[i].perfect_score); total_points += assignments[i].score; total_perfect_points += assignments[i].perfect_score; } printf("\n%d assignments in all, %.2f total points, %.2f perfect score\n", num_assignments, total_points, total_perfect_points); printf("average %.2f\n", 100 * total_points / total_perfect_points); return 0; } bool parse_line(char *line, assignment_t *a) { /* * I could have written "%20s %lf %lf" for the format string, * but then I'd have to remember that if I changed the size of * the field in the struct I'd need to change it here too. */ if (sscanf(line, FMT_NAME"%lf %lf", a->name, &a->score, &a->perfect_score) == 3) return true; else return false; } int compare_assignments(const void *e1, const void *e2) { /* compare names in *e1, *e2 */ assignment_t *a1 = (assignment_t *) e1; assignment_t *a2 = (assignment_t *) e2; return strcmp(a1->name, a2->name); }