Parser only reads two fields out of the intended 7

ghz 8months ago ⋅ 89 views

Source code:

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <conio.h>
#include <windows.h>
#include "addon.h"

static book* archivelib;
static book* noticelib;
const int MAX_LINE_LENGTH = (255);

void fileCheck(file sourcefile) { //Checks if a file exists
    if (access(sourcefile.filename, F_OK) != false) {
        textcolour(RED);
        fprintf(stderr, "Error: %s does not exist.\n", sourcefile.filename);
        exit(EXIT_FAILURE);
    };
};

size_t recordcount(file source) { //Finds how many records are in a file
    source.fp = fopen(source.filename, "r");
    char ch;
    size_t records = 0;
    fseek(source.fp, START_OF_LINE, SEEK_SET);
    while ((ch = fgetc(source.fp)) != EOF)
        if (ch == '\n')
            records++;
    fclose(source.fp);
    return records;
};

void textcolour(int colour) {
    static int __BACKGROUND;
    HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO csbiInfo;

    GetConsoleScreenBufferInfo(h, &csbiInfo);

    SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE),
                             colour + (__BACKGROUND << 4));
}

void cls(HANDLE hConsole) {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    SMALL_RECT scrollRect;
    COORD scrollTarget;
    CHAR_INFO fill;

    // Get the number of character cells in the current buffer.
    if (!GetConsoleScreenBufferInfo(hConsole, &csbi)) {
        return;
    }

    // Scroll the rectangle of the entire buffer.
    scrollRect.Left = 0;
    scrollRect.Top = 0;
    scrollRect.Right = csbi.dwSize.X;
    scrollRect.Bottom = csbi.dwSize.Y;

    // Scroll it upwards off the top of the buffer with a magnitude of the entire height.
    scrollTarget.X = 0;
    scrollTarget.Y = (SHORT)(0 - csbi.dwSize.Y);

    // Fill with empty spaces with the buffer's default text attribute.
    fill.Char.UnicodeChar = TEXT(' ');
    fill.Attributes = csbi.wAttributes;

    // Do the scroll
    ScrollConsoleScreenBuffer(hConsole, &scrollRect, NULL, scrollTarget, &fill);

    // Move the cursor to the top left corner too.
    csbi.dwCursorPosition.X = 0;
    csbi.dwCursorPosition.Y = 0;

    SetConsoleCursorPosition(hConsole, csbi.dwCursorPosition);
}

void clearscreen()  {
    HANDLE hStdout;
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    cls(hStdout);
}

const book emptybook = {
    .checkdigit = (0),
    .ISBN = "empty",
    .title = "empty",
    .DOR = "empty",
    .PersonDetails = { .firstname = "empty", .lastname = "empty", .fullname = "empty"},
    .status = "empty",
};

static file notice = {
    .filename = "NOTICE.txt",
    .sizeofline = MAX_LINE_LENGTH,
    .field = { .format = "%17[^|]|%55s|%10s|%10s|%21s|%10[^|]|%26[^\n]\n", .num = (7)},
};

static file archive = {
    .filename = "ARCHIVE.txt",
    .sizeofline = MAX_LINE_LENGTH,
    .field = { .format = "%17[^|]|%55s|%10s|%10s|%21s|%10[^|]|%26[^\n]\n", .num = (7)},
};

void printBook(book bk) { //Print information on book bk
    if (strcmp(bk.title, bk.PersonDetails.firstname) == 0) {
        textcolour(RED);
        fprintf(stderr, "Error: The book could not be printed\n");
        sleep(2);
        clearscreen();
        return;
    };

    printf("|ISBN:            %s\n", bk.ISBN);
    printf("|Book:            %s\n", bk.title);
    printf("|Authour:         %s\n", bk.PersonDetails.fullname);
    printf("|Date of Release: %s\n", bk.DOR);
};

book* alloclib(file libfile) {
    fileCheck(libfile); //check the file
    libfile.records = recordcount(libfile); //find the number of records in file

    book* lib = malloc(libfile.records * sizeof *lib);
    if (lib == NULL) {
        textcolour(RED);
        fprintf(stderr, "Memory allocation failed!\n");
        exit(EXIT_FAILURE);
    };
    return(lib);
}

int CmpBookRecs(const void* recA, const void* recB) { //Compare book records alphabetically
    int result = 0;
    book* bkRecA = (book*)recA;
    book* bkRecB = (book*)recB;

    //Compare the titles
    result = strcmp(bkRecA->title,
                    bkRecB->title);

    //If (titles match) compare the names
    if (!result)
        result = strcmp(bkRecA->PersonDetails.lastname,
                        bkRecB->PersonDetails.lastname);

    //If (names match) compare the ISBN
    if (!result)
        result = strcmp(bkRecA->ISBN,
                        bkRecB->ISBN);
    return (result); //Return the the result
};

book* loadlibrary(file libsource) { //Loads all the books in "libarchive"
    printf("Loading library...\n");
    sleep(2);

    libsource.fp = fopen(libsource.filename, "r");
    if (libsource.fp == NULL) {
        textcolour(RED);
        fprintf(stderr, "Error opening file.\n"); //Return an error if the file cannot be opened
        exit(EXIT_FAILURE);
    };

    int read = 0; //read will be used to ensure each line/record is read correctly
    int records = 0; //records will keep track of the number of Student records read from the file

    char* token; //smaller char* broken from line
    const char* del = "-"; //the delimiter the line is broken into a series of tokens by

    //Read all records from the file and store them into the lib
    book* lib = alloclib(libsource);
    do {
        //Check how many fields are in the file
        read = fscanf(libsource.fp, libsource.field.format, lib[records].ISBN,
                                                            lib[records].title,
                                                            lib[records].PersonDetails.firstname,
                                                            lib[records].PersonDetails.lastname,
                                                            lib[records].PersonDetails.fullname,
                                                            lib[records].DOR);
        //If fscanf read values from the file, assign values then successfully add an another record
        if (read == libsource.field.num)
            records++;

        //If read is not equal the field size and it is not the end of file
        if (read != libsource.field.num && !feof(libsource.fp)) {
            textcolour(RED);
            fprintf(stderr, "File format incorrect. %d fields read instead of %d\n", read, libsource.field.num);
            sleep(2);
            exit(EXIT_FAILURE);
        };

        //If there was an error reading from the file exit with an error message and status
        if (ferror(libsource.fp)) {
            textcolour(RED);
            fprintf(stderr, "Error in reading file.\n");
            exit(EXIT_FAILURE);
        };
        lib[records].checkdigit = atoi(strrchr(lib[records].ISBN, '-'));
    } while (!feof(libsource.fp) && records < libsource.records);

    textcolour(LIGHTGREEN);
    printf("Sorting Library...\n");
    sleep(2);
    qsort(lib, records, sizeof(*lib), CmpBookRecs);
    textcolour(GREEN);
    printf("Library was sorted!\n\n");

    //free(line);
    fclose(libsource.fp);
    printf("Library was successfully loaded...\n");
    sleep(2);
    return (lib);
};

int CheckISBN(char* ISBN) { //Calculates the check digit for 13 digit International Standard Book Number
    int x[13];
    int count = 0;
    int i = 0;

    while (count < strlen(ISBN) && ISBN[i] != '\0') {
        if (isdigit(ISBN[i])) {
            x[count] = ISBN[i] - '0'; // Convert character to integer
            count++;
        } i++;
    }

    int s = 0,
        t = 0;

    //Calculation
    for (i = 0; i < 12; ++i) {
        if (i % 2 == 0) {
            t += (x[i] * 3);
        } else {
            t += x[i];
        } s += t;
    };

    int r = s % 10;
    return (10 - r);
};

book booksearch(file libsource, book* lib) { //Allows the user to search for a specify book based on its title or ISBN
    book foundbook; //book to be found
    char* search = malloc((MAX_LINE_LENGTH + 1) * sizeof *search); //search title
    char information[MAX_LINE_LENGTH]; //book title in record

    char* option;
    bool flag = true;
    do { //Infinite loop until input
        printf("\n");
        textcolour(BLUE);
        printf(" |     _____   ___   ____  ____      __  __ __             |\n");
        printf(" |    / ___/  /  _] /    ||    `    /  ]|  |  |            |\n");
        printf(" |   (   |_  /  [_ |  o  ||  D  )  /  / |  |  |            |\n");
        printf(" |    |__  ||    _]|     ||    /  /  /  |  _  |            |\n");
        printf(" |    /  ` ||   [_ |  _  ||    ` /   |_ |  |  |            |\n");
        printf(" |    `    ||     ||  |  ||  .  ``     ||  |  |            |\n");
        printf(" |     |___||_____||__|__||__||_| |____||__|__|            |\n");
        printf(" |     ___   ____   ______  ____   ___   ____    _____     |\n");
        printf(" |    /   ` |    ` |      ||    | /   ` |    `  / ___/     |\n");
        printf(" |   |     ||  o  )|      | |  | |     ||  _  |(   |_      |\n");
        printf(" |   |  O  ||   _/ |_|  |_| |  | |  O  ||  |  | |__  |     |\n");
        printf(" |   |     ||  |     |  |   |  | |     ||  |  | /  ` |     |\n");
        printf(" |   |     ||  |     |  |   |  | |     ||  |  | `    |     |\n");
        printf(" |    |___/ |__|     |__|  |____| |___/ |__|__|  |___|     |\n");
        printf(" |                                                         |\n");

        printf(" |- [");

        textcolour(LIGHTBLUE);
        printf("1");

        textcolour(BLUE);
        printf("] ISBN                                               |\n");
        printf(" |- [");

        textcolour(LIGHTBLUE);
        printf("2");

        textcolour(BLUE);
        printf("] Title                                              |\n");
        printf(" |- [");

        textcolour(LIGHTBLUE);
        printf("3");

        textcolour(BLUE);
        printf("] Exit                                               |\n\n");

        //User option
        switch (getc(stdin)) {
            case '1': option = "ISBN";
                      flag = false;
                      break;
            case '2': option = "Title";
                      flag = false;
                      break;
            case '3': return(emptybook); //Exit
                      break;
            default : textcolour(RED); //Red
                      fprintf(stderr, "\nInvalid option. Try again.");
                      clearscreen();
                      break;
        };
    } while (flag == true);

    //Prompt user for the book they want to search
    textcolour(YELLOW);
    clearscreen();
    sleep(2);
    printf("\nPlease enter the %s of the book you are searching for:\n", option);
    scanf("%s", search);
    //fgets(search, strlen(search), stdin);

    //Remove newline character if exists
    if ((strlen(search) > 0) && (search[strlen(search) - 1] == '\n'))
        search[strlen(search) - 1] = '\0';

    //Check if title is proper
    if (strcmp(option, "Title") != 0) {
        //Convert to search to all caps
        for (int i = 0; search[i] != '\0'; i++) {
            //If any character in the char* is not a character or char* is empty
            if (isalpha(search[i]) == 0 || search == NULL)
                goto impromperformat;
            search[i] = toupper(search[i]);
        };
    };

    //Check if ISBN is correct
    if (strcmp(option, "ISBN") == 0) {
        int q[5];
        int read = sscanf(search, "%3d-%2d-%4d-%3d-%1d", q[0],q[1],q[2],q[3],q[4]);
        if (read != 5) {
            impromperformat:
                textcolour(RED);
                fprintf(stderr, "This not a %s. Returning...\n", option);
                return(emptybook);
        }

        if (read == 5 && q[4] != CheckISBN(search)) {
            textcolour(RED);
            fprintf(stderr, "This not a proper ISBN. Returning...\n", option);
            return(emptybook);
        }
    };

    int records = 0; //the number of lines in the record
    bool found = false; //if the the book was found
    int linenumber = 0;
    const char* del = "|"; //character that seperates each field

    //While there are book records that have not been read
    while (fgets(information, libsource.sizeofline, libsource.fp) != NULL && linenumber != notice.records) {
        for (int i = 0; information[i] != '\n'; i++)
            information[i] = toupper(information[i]);

        //If the an occurrence of the substring, (search) in the string, (line) is found
        if (strstr(information, search) != NULL) {
            int read = sscanf(information, libsource.field.format, lib[records].ISBN,
                                                                   lib[records].title,
                                                                   lib[records].PersonDetails.firstname,
                                                                   lib[records].PersonDetails.lastname,
                                                                   lib[records].PersonDetails.fullname,
                                                                   lib[records].DOR);
            if (read != 5)
                goto notfound;

            foundbook.checkdigit = atoi(strrchr(foundbook.ISBN, '-'));
            if (foundbook.checkdigit != CheckISBN(foundbook.ISBN))
                goto notfound;

            if (strcmp(foundbook.status, "Available") != 0) {
                textcolour(RED);
                fprintf(stderr, "This book is currently unavailable.\n");
                return(emptybook);
            };

            textcolour(LIGHTGREEN);
            printf("A match was found for %s!\n", search); //Book is found
            found = true;
        } linenumber++;
    };

    notfound:
        //free(token); //Free up book title
        free(search); //Free up search

    if (found == false) {
        textcolour(LIGHTRED);
        printf("Sorry, %s is currently not available. Better add one to the bookshelf!\n", search);
        sleep(2);
        return(emptybook); //return an empty book if found is still false
    };
};

void titleview(book* lib, size_t libsize) {
    lib = alloclib(notice);
    lib = loadlibrary(notice);

    textcolour(CYAN);
    printf("| These are all records of the book currently at the library: \n");
    for (int records = 0; records < libsize; records++) {
        while (lib[records].checkdigit != CheckISBN(lib[records].ISBN))
            records++; //Go to next record skipping the incorrect record
        textcolour(BLUE);
        printf("%d) ", records);

        textcolour(CYAN);
        if (records % 2 == 0)
            textcolour(LIGHTBLUE);
        printBook(lib[records]);
        sleep(2);
        printf("\n");
    };
};

void menu(void) { //Program Menu
    book found;
    do { //Infinite loop until input
        printf("\n");
        textcolour(YELLOW);
        printf(" ___________________________________________________________________________________________________________________________________\n");
        printf(" |-     __  ___                __                            __            _____                                  _   __            -|\n");
        printf(" |-    /  |/  / ___  ___ _ ___/ / ___  _    __ _  __ ___ _  / / ___       / ___/ ___   __ _   __ _  __ __  ___   (_) / /_  __ __    -|\n");
        printf(" |-   / /|_/ / / -_)/ _ `// _  / / _ `| |/|/ /| |/ // _ `/ / / / -_)     / /__  / _ ` /  ' ` /  ' `/ // / / _ ` / / / __/ / // /    -|\n");
        printf(" |-  /_/  /_/  |__/ |_,_/ |_,_/  |___/|__,__/ |___/ |_,_/ /_/  |__/      |___/  |___//_/_/_//_/_/_/|_,_/ /_//_//_/  |__/  |_, /     -|\n");
        printf(" |-                                                                                                                      /___/      -|\n");
        printf(" |-     __    _    __                                  ____              __                                                         -|\n");
        printf(" |-    / /   (_)  / /   ____ ___ _  ____  __ __       / __/  __ __  ___ / /_ ___   __ _                                             -|\n");
        printf(" |-   / /__ / /  / _ ` / __// _ `/ / __/ / // /      _` `   / // / (_-</ __// -_) /  ' `                                            -|\n");
        printf(" |-  /____//_/  /_.__//_/   |_,_/ /_/    |_, /      /___/   |_, / /___/|__/ |__/ /_/_/_/                                            -|\n");
        printf(" |-                                     /___/              /___/                                                                    -|\n");
        printf(" |-                                                                                                                                 -|\n");
        printf(" |   [1] View Upcoming Titles                                                                                                        |\n");
        printf(" |   [2] Book Search                                                                                                                 |\n");
        printf(" |   [3] Exit                                                                                                                        |\n");

        //User option
        switch (getc(stdin)) {
            case '1': clearscreen();
                      sleep(2);
                      titleview(noticelib, notice.records);
                      break;
            case '2': clearscreen();
                      sleep(2);
                      found = booksearch(archive, archivelib); //Allows user to search a book
                      printBook(found);
                      break;
            case '3': exit(0); //Exit
                      break;
            default : textcolour(RED);
                      fprintf(stderr, "Invalid option. Try again.");
                      sleep(2);
                      clearscreen();
        };
    } while(1);
};

int main() {
    noticelib = alloclib(notice);
    archivelib = alloclib(archive);
    menu();
    free(archivelib); //free the archivelib
    free(noticelib); //free the noticelib
    return 0;
};

And my custom header:

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <conio.h>
#include <windows.h>
#include <stdio.h>

#ifndef __ADDON_H__
#define __ADDON_H__

#define MAX_PATH_LENGTH (255)
const int FIRST_LINE = 1;
const int START_OF_LINE = 0;

typedef struct {
    size_t records;
    size_t sizeofline;
    char* filename;
    FILE* fp;
    struct {
        int num;
        char* format;
    } field;
} file;

typedef struct {
    int checkdigit; //permits mathematical checking for accuracy
    char ISBN[18]; //ISBN number
    char title[76]; //Title of material
    char DOR[11]; //Date of release for the item
    struct {
        char firstname[11]; //first name
        char lastname[11]; //last name
        char fullname[22]; //First + Middle + Last name of Authour
    } PersonDetails; //details about a person
    char status[27];
} book; //structure for a book

enum {
    BLACK = 0,
    BLUE,
    GREEN,
    CYAN,
    RED,
    MAGENTA,
    BROWN,
    LIGHTGRAY,
    DARKGRAY,
    LIGHTBLUE,
    LIGHTGREEN,
    LIGHTCYAN,
    LIGHTRED,
    LIGHTMAGENTA,
    YELLOW,
    WHITE
};

#endif //__ADDON_H__

I've been trying to get the number of fields that could successfully be scanned in my file as read in the function loadlibrary. However, it only reads two fields out of the intended 7.

Any corrections on how to fix the error or improve my code are welcome. Thank you!

Here is the data I am currently testing it on: ARCHIVE.txt

ISBN|Title|First Name|Last Name|Full Name|DOR|Status
978-51-7947-772-3|"Bleed Magic"|Orville|Baker|Orville Baker|15/04/2014|Available
978-05-0191-800-4|"Solar Waltz"|Alejandra|Bauer|Alejandra Bauer|30/07/1985|Unavailable - Rented
978-43-8687-725-8|"From Halsey to Hal's Island"|Jacklyn|Bruce|Jacklyn Bruce|09/05/1996|Available
978-36-2955-426-0|"Witch With Honor"|Miriam|Bryant|Miriam Bryant|14/07/1992|Unavailable - Damaged
978-95-2736-473-4|"Point A to Z"|Sang|Cherry|Sang Cherry|28/07/1987|Unavailable - Library use only
978-45-0742-523-8|"Priest named Sins"|Teddy|Decker|Teddy Decker|15/09/1989|Unavailable - Missing
978-27-1385-463-7|"Success of the Solstice"|Sandra|Duncan|Sandra Duncan|24/04/1989|Unavailable - In Process
978-31-8143-556-4|"What’s Over There?"|Leonel|Espinoza|Leonel Espinoza|25/03/2003|Available
978-95-5238-240-6|"The Amazing Adventures of Ice-Boy: Robots of Everest"|Ingrid|Guerra|Ingrid Guerra|24/06/2004|Available
978-85-7786-850-6|"One Boy And The World!"|Ezequiel|Hall|Ezequiel Hall|01/11/1994|Unavailable - Rented
978-28-5593-358-0|"Origin Of The Fog"|Nicky|Hancock|Nicky Hancock|23/08/2013|Available
978-33-2798-076-7|"Signs of the Past"|Lynwood|Hardin|Lynwood Hardin|27/02/1992|Available
978-23-8499-409-0|"Bleeding At The Past"|Renee|Johnston|Renee Johnston|07/05/1993|Available
978-32-9910-416-1|"Vision of Evil"|Jon|Kirby|Jon Kirby|16/02/2007|Unavailable - Rented

NOTICE.txt

ISBN|Title|First Name|Last Name|Full Name|DOR|Status
978-89-8818-596-4|"London Titans: As We Still Exist"|Kieth|Kirk|Kieth Kirk|08/08/1996|Unavailable - Coming Soon
978-30-7350-796-6|"Linger Longer"|Ester|Li|Ester Li|05/07/2022|Unavailable - Coming Soon
978-87-6109-805-4|"Till You, My Wallflower, Are Blooming"|Penny|Lucero|Penny Lucero|06/08/2018|Unavailable - Coming Soon
978-44-5008-783-5|"Fin and Tail, Claw and Tooth"|Bernadine|Mckinney|Bernadine Mckinney|18/10/2023|Unavailable - Coming Soon
978-60-3893-332-9|"Yes... Maybe? No!"|Vicky|Mills|Vicky Mills|05/02/2008|Unavailable - Coming Soon
978-55-4205-071-3|"Mermaids and Sirens"|Genevieve|Ochoa|Genevieve Ochoa|01/07/1987|Unavailable - Coming Soon
978-66-8758-265-7|"O Death, Where Is Thy Sting?"|Mohamed|Oconnell|Mohamed Oconnell|16/02/2021|Unavailable - Coming Soon
978-68-4196-147-2|"Behind the Door"|Hunter|Payne|Hunter Payne|19/05/2010|Unavailable - Coming Soon
978-60-6519-219-5|"Stay Hidden"|Saul|Powers|Saul Powers|22/12/2004|Unavailable - Coming Soon
978-90-3065-807-8|"The Things that I Never Will Learn"|Eva|Robertson|Eva Robertson|18/06/2007|Unavailable - Coming Soon
978-74-8013-934-5|"Humans With Us Untold Gods"|Suzette|Rosales|Suzette Rosales|25/02/1987|Unavailable - Coming Soon
978-87-4206-938-7|"Whispers of a Ghost"|Diego|Russo|Diego Russo|03/06/1996|Unavailable - Coming Soon
978-66-2724-397-6|"When Love Lasts"|Barbra|Sanders|Barbra Sanders|29/11/2018|Unavailable - Coming Soon
978-78-4619-100-0|"Pelicans We"|Sheila|Whitehead|Sheila Whitehead|18/11/1982|Unavailable - Coming Soon
978-09-4189-874-4|"Obsidian Leviathan"|Elwood|Wise|Elwood Wise|10/05/2023|Unavailable - Coming Soon
978-20-7104-860-0|"Of Fire and Silk"|Reyes|Wolf|Reyes Wolf|27/02/2001|Unavailable - Coming Soon

Answers

It seems like there are a few issues in your code that might be causing the problem with reading only two fields instead of seven in the loadlibrary function. Here are some potential corrections and improvements:

  1. Correct the recordcount function: The recordcount function seems to count the number of lines in a file to determine the number of records. However, it's reading the file character by character, which might not be efficient or accurate, especially if the file contains multiline records. Instead, you can use a line-based approach to count the number of records.

  2. Correct the fileCheck function: In the fileCheck function, you're checking if the file exists using the access function, but you're comparing the result to false, which might not work as expected. The access function returns 0 on success and -1 on failure. So, you should compare the result to -1.

  3. Check the file format: Ensure that the format specifier in the fscanf function matches the format of the lines in your file exactly. Any discrepancies between the format specifier and the actual format of the lines can cause issues with reading the fields correctly.

  4. Use dynamic memory allocation for strings: Instead of defining fixed-size character arrays for strings like ISBN, title, etc., consider using dynamic memory allocation (e.g., malloc) to allocate memory based on the length of the input strings. This will ensure that you can handle strings of varying lengths without overflow issues.

  5. Check for memory allocation failures: Whenever you allocate memory dynamically using malloc, make sure to check if the allocation was successful before using the allocated memory. If malloc returns NULL, it means that the allocation failed, and you should handle this error condition appropriately.

  6. Handle file errors gracefully: When opening files using fopen, check if the file was opened successfully before performing any operations on it. If fopen returns NULL, it means that the file opening failed, and you should handle this error condition appropriately.

  7. Use meaningful variable names: Use meaningful variable names that accurately describe their purpose and usage. For example, instead of lib, you can use library to represent a library of books.

  8. Avoid using goto: The use of goto is generally discouraged in modern programming practices because it can make the code harder to understand and maintain. Consider using structured control flow constructs like loops and conditional statements instead.

By addressing these issues and making the necessary corrections, you should be able to improve the functionality and reliability of your code. Let me know if you need further assistance with any specific part of the code!