Discuss the good, the bad and the ugly aspects of their code.
Please be gentle in any criticism - we are all learning!
Have we learnt anything we think would be useful to share with the tutorial?
Include appropriate #defines.
Include a field allowing a linked list to be formed from student structs.
#define MAX_STUDENT_NAME_LENGTH 128 #define MAX_GRADE_STRING_LENGTH 22 #define MAX_LAB_NAME_LENGTH 32 struct student { int zid; char name[MAX_STUDENT_NAME_LENGTH + 1]; char lab_name[MAX_LAB_NAME_LENGTH + 1]; char lab_grades[MAX_GRADE_STRING_LENGTH + 1]; struct student *next; };
struct student *read_student(FILE *stream)which mallocs a student struct and assigns values to its fields, from values read from a line of the stream, it is given as an argument.
Write a function with this prototype:
struct student *read_students_file(char filename[])which opens the file it is given as argument and calls read_student to create student structs containing the information in the file.
read_students_file should return these student structs as a linked list.
struct student *read_students_file(char filename[]) { FILE *fp = fopen(filename, "r"); if (fp == NULL) { fprintf(stderr,"warning file %s could not be opened for reading\n", filename); return NULL; } struct student *first_student = NULL; struct student *last_student = NULL; struct student *s; while ((s = read_student(fp)) != NULL) { if (last_student == NULL) { first_student = s; last_student = s; } else { last_student->next = s; last_student = s; } } return first_student; }
struct student *read_student(FILE *stream)which mallocs a student struct and assigns values to its fields from values read from a line of the the stream it is given as argument.
The file will contain lines specifing zid, student name, lab name and lab grades in this format:
5099703 Tsun Bordignon thu13-sitar A+A+CABAB..A.read_student should return a pointer to the newly created student struct, or NULL if it can not read a line.
struct student *read_student(FILE *stream) { char line[MAX_LINE_LENGTH]; struct student *s = malloc(sizeof (struct student)); assert(s); if (fgets(line, MAX_LINE_LENGTH, stream) == NULL) { free(s); return NULL; } char *newline_ptr = strchr(line, '\n'); assert(newline_ptr); *newline_ptr = '\0'; char *space_ptr = strrchr(line, ' '); assert(space_ptr); strncpy(s->lab_grades, space_ptr + 1, MAX_GRADE_STRING_LENGTH); s->lab_grades[MAX_GRADE_STRING_LENGTH] = '\0'; *space_ptr = '\0'; space_ptr = strrchr(line, ' '); assert(space_ptr); strncpy(s->lab_name, space_ptr + 1, MAX_LAB_NAME_LENGTH); s->lab_name[MAX_LAB_NAME_LENGTH] = '\0'; *space_ptr = '\0'; space_ptr = strchr(line, ' '); assert(space_ptr); strncpy(s->name, space_ptr + 1, MAX_STUDENT_NAME_LENGTH); s->name[MAX_STUDENT_NAME_LENGTH] = '\0'; *space_ptr = '\0'; s->zid = atoi(line); s->next = NULL; return s; }
Your tutor may still choose to cover some of the questions time permitting.
splitString
which takes a string read by fgets containing
substrings separated by commas, places the substrings in an char[][] array and
returns the number of strings.
You can assume there at most 128 words on the line and no word is more than 32 characters long.
printSubstrings
which prints one per line the substrings in
the char[][] array created in the previous question.
substringsSorted
which given the char[][] array in the previous question
returns 1 if the substrings are increasing order and 0 otherwise.
searchSubstrings
which given the char[][] array in the previous question
returns 1 if the array contains a specified string and 0 otherwise.
substring.c
which reads lines using fgets and calls the above functions.
substring.c
// Written by Costa Paraskevopoulos as a // COMP1511 tutorial solution #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NUM_WORDS 128 #define MAX_WORD_LEN 33 // including null char // accounts for 128 * 32 "word" characters + 127 commas + 1 null char #define MAX_LINE_LEN (MAX_NUM_WORDS * MAX_WORD_LEN) int splitString(char *src, char dest[MAX_NUM_WORDS][MAX_WORD_LEN]); void printSubstrings(int numSubstrings, char strings[MAX_NUM_WORDS][MAX_WORD_LEN]); int substringsSorted(int numSubstrings, char strings[MAX_NUM_WORDS][MAX_WORD_LEN]); int searchSubstrings(int numSubstrings, char *searchStr, char strings[MAX_NUM_WORDS][MAX_WORD_LEN]); int main(int argc, char *argv[]) { char line[MAX_LINE_LEN]; int lineNo = 1; while (fgets(line, MAX_LINE_LEN, stdin) != NULL) { char substrings[MAX_NUM_WORDS][MAX_WORD_LEN]; int numWords = splitString(line, substrings); printf("%d words on line %d:\n", numWords, lineNo); printSubstrings(numWords, substrings); if (substringsSorted(numWords, substrings) != 0) { printf("Substrings are in increasing order\n"); } else { printf("Substrings are not in increasing order\n"); } // search for some common test strings char *searches[4] = {"hello", "world", "test", "qwerty"}; for (int i = 0; i < 4; i++) { if (searchSubstrings(numWords, searches[i], substrings)) { printf("Found \"%s\" on line %d\n", searches[i], lineNo); } else { printf("\"%s\" not found on line %d\n", searches[i], lineNo); } } printf("\n"); lineNo++; } return EXIT_SUCCESS; } // Extracts words separated by commas and places them in dest int splitString(char *src, char dest[MAX_NUM_WORDS][MAX_WORD_LEN]) { int numWords = 0; int i, j; for (i = 0; i < MAX_LINE_LEN && numWords != MAX_NUM_WORDS; i++) { // reached end of src if (src[i] == '\n' || src[i] == '\0') { break; } // copy current word over to dest for (j = i; j - i < MAX_WORD_LEN - 1; j++) { if ((src[j] == ',' || src[j] == '\n' || src[j] == '\0') && i != j) { dest[numWords][j - i] = '\0'; numWords++; i = j; // start next word break; } else if (src[j] == ',') { break; // skip empty words } dest[numWords][j - i] = src[j]; } // terminate long word if (i != j) { dest[numWords][j - i] = '\0'; numWords++; i = j; // skip rest of word while (src[i] != '\0' && src[i] != '\n' && src[i] != ',') { i++; } } } return numWords; } // Prints all words in strings on separate lines void printSubstrings(int numSubstrings, char strings[MAX_NUM_WORDS][MAX_WORD_LEN]) { for (int i = 0; i < numSubstrings; i++) { printf("%s\n", strings[i]); } } // Returns 1 if strings is sorted; 0 otherwise int substringsSorted(int numSubstrings, char strings[MAX_NUM_WORDS][MAX_WORD_LEN]) { for (int i = 1; i < numSubstrings; i++) { if (strcmp(strings[i], strings[i - 1]) < 0) { return 0; } } return 1; } // Returns 1 if searchStr is in strings; 0 otheriwse int searchSubstrings(int numSubstrings, char *searchStr, char strings[MAX_NUM_WORDS][MAX_WORD_LEN]) { for (int i = 0; i < numSubstrings; i++) { if (strcmp(strings[i], searchStr) == 0) { return 1; } } return 0; }