COMP1511 17s1 Introduction to Programming
  1. Your tutor has asked a lab pair to present their week 10 work.

    Discuss the good, the bad and the ugly aspects of their code.

    Please be gentle in any criticism - we are all learning!

  2. Did you blog last week? What is this week's blogging theme?
  3. How are we progressing with the assignment?

    Have we learnt anything we think would be useful to share with the tutorial?

  4. Define a struct student which your tutor could use to store lab grades for a COMP1511 student.

    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;
    };
    
  5. Assume you have a function with the following prototype
    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;
    }
    
  6. Write a function with this prototype:
    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;
    }
    

    Revision questions

    The remaining tutorial questions are primarily intended for revision - either this week or later in session.

    Your tutor may still choose to cover some of the questions time permitting.

    • Write a C function 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.

    • Write a C function printSubstrings which prints one per line the substrings in the char[][] array created in the previous question.
    • Write a C function substringsSorted which given the char[][] array in the previous question returns 1 if the substrings are increasing order and 0 otherwise.
    • Write a C function searchSubstrings which given the char[][] array in the previous question returns 1 if the array contains a specified string and 0 otherwise.
    • Write a program substring.c which reads lines using fgets and calls the above functions.
    Sample solution for 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;
    }