解析器仅读取预期 7 个字段中的两个字段

问题描述 投票:0回答:1

源代码:

#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;
};

还有我的自定义标题:

#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__

我一直在尝试获取在函数 loadlibrary 中读取的文件中可以成功扫描的字段数。然而,它只读取了预期 7 个字段中的两个字段。

欢迎对如何修复错误或改进我的代码进行任何更正。谢谢!

这是我目前正在测试的数据: 存档.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

通知.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
c scanf
1个回答
1
投票

你的程序对我来说失败了`loadlibrary()对我来说:

Number of fields read: 0
File format incorrect.

因为

argscheck()
是用指针
lib[records].ISBN
...
lib[records].status
调用的。由于
lib
malloc()
一起分配,这些指针未初始化,并且会导致
vfscanf()
中的未定义行为,该指针指向“足够长以容纳输入序列和终止空字节的字符数组”。在我的系统上,这些指针恰好为 NULL,并且
vfscanf()
返回 0。我怀疑它们在您的系统上不是 NULL,这可能会导致内存损坏。

这是您应该发布的最小示例:

#include <stdio.h>
#include <stdlib.h>

#define LEN 99
#define str(s) str2(s)
#define str2(s) #s

int main() {
    FILE *f = fopen("ARCHIVE.txt", "r");
    char *a[1]; // uninitialized
    // a[0] = malloc(LEN+1);
    int read = fscanf(f, "%" str(LEN) "s, ", a[0]);
    printf("%d\n", read);
    fclose(f);
}

将字符串读入使用

scanf()
系列分配的数组时,始终指定最大字段宽度,否则长输入将导致缓冲区溢出。

如果您的

scanf()
支持“m”字符,那么
scanf()
可以为您分配这些数组,即
scanf("%ms, ", &a[0])
。如果您的输入不受信任,我仍然会指定最大字段宽度,以避免分配巨大的变量。顺便说一句,这将读取一个单词(即直到第一个空格),其中包括逗号,这不是您所期望的。

我还建议你将 I/O 和解析代码分开。两者都很棘手,混合起来会使事情变得更加困难。每个 I/O 调用都可能失败(永远失败;就像您读到

EOF
时一样)。 CSV 文件可能包含嵌入的换行符,因此我建议您将整个文件读入缓冲区(
realloc()
根据需要使用数组来假定文件的最大大小)。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.