c чтение отдельных слов из текстового файла с помощью fscanf()

Я пишу программу викторины. Программа должна прочитать вопрос, ответы и правильный ответ из CSV-файла. Затем он должен хранить их в массиве.

 void read(char question[][50], char answer1[10][10], char answer2[10][10], char answer3[10][10], char answer4[10][10], int correctAnswer[10], int *size, char fileName[], int noOfQuestion){
FILE *reader;
int count;
char qBuffer[50];
char ansBuffer1[50];
char ansBuffer2[50];
char ansBuffer3[50];
char ansBuffer4[50];
int iBuffer = 0;
*size = 0;


//open file
reader = fopen(fileName, "r");

//checking file is open or not
if (reader == NULL)
{
    printf("Unable to open file %s", fileName);
}
else
{
    fscanf(reader, "%100[^\t*\?,],%[^,],%[^,],%[^,],%[^,],%d", size);
    for (count = 0; feof(reader) == 0 && count<*size && count<noOfQuestion; count++){
    //Reading file
        fscanf(reader, "%100[^\t*\?,],%[^,],%[^,],%[^,],%[^,],%d", qBuffer, ansBuffer1, ansBuffer2, ansBuffer3, ansBuffer4, iBuffer);

        //Storing data
        strcpy(question[count], qBuffer);
        strcpy(answer1[count], ansBuffer1);
        strcpy(answer2[count], ansBuffer2);
        strcpy(answer3[count], ansBuffer3);
        strcpy(answer4[count], ansBuffer4);
        correctAnswer[count] = iBuffer;

        // Check Correct Number of Items Read
        if( count == noOfQuestion )
        {
            printf("There are more items in the file than MaxNoItems specifies can be stored in the output arrays.\n\n");
            *size = count;
        }
        else if( count != *size - 1 )
        {
            printf("File is corrupted. Not as many items in the file as specified at the top.\n\n");
            *size = count;
        }
        //Break if reached end of file.
        if (feof(reader))
        { break;}
        }
        fclose(reader);
}
}

Это CSV-файл для чтения. каждый вопрос и ответы в одной строке.

What function do you use to open a file?,fscanf,fclose,fopen,main,3
Which of the following is not a variable type?,int,float,char,string,4
How many bytes is a character?,8,4,2,1,4
What programming language have you been studying this term?,B,A,D,C,4
Which of the following is a comment?,#comment,//comment,$comment,%comment,2
Which of these is in the C Standard Library?,stdio.h,studio.h,iostream,diskio.h,1
What tool do we use to compile?,compiler,builder,linker,wrench,1
What function do you use to close a file?,fscanf,fclose,fopen,main,2
How do you include a file?,#include,//include,$include,%include,1
What are you doing this quiz on?,paper,whiteboard,computer,chalkboard,3

1 ответ

Я работал над тем, чтобы найти способ решить проблемы в вашем коде, однако просто не существует четкого способа следить за вашим двойным чтением каждой строки и заставить его работать разумным образом. У вас есть структурная проблема: вы пытаетесь прочитать строку дважды, сначала, чтобы определить размер, а затем попытаться прочитать фактические значения. Это имеет много подводных камней.

Вместо того, чтобы пытаться читать каждую строку по частям, гораздо лучше читать всю строку за раз, используя строковые функции ввода, предоставляемые C (fgets, getline). Это сделает ваш код намного более гибким и облегчит вам жизнь. Основной подход заключается в том, чтобы читать строку за раз в "буфер", затем использовать предоставленные инструменты, извлекать то, что вам нужно, из строки, сохранять ее таким образом, который имеет смысл, и переходить к следующей строке.

Просто нет способа жестко закодировать несколько массивов в вашем списке аргументов функции и заставить его работать в здравом уме. Правильный способ сделать это - передать указатель на некоторый тип данных в вашу функцию, заполнить его функцией, распределить память по мере необходимости и предоставить указатель взамен. В вашем случае простая структура имеет гораздо больше смысла, чем один двумерный массив для каждого вопроса, который вы ожидаете прочитать.

Гораздо лучше define начальный размер для ожидаемого количества вопросов, (MAXQ128 ниже), и выделить хранилище для этой суммы. Вы можете сделать то же самое для ожидаемых ответов на вопрос (MAXA16 ниже). Если вы в конечном итоге читаете больше, чем каждый, вы можете легко перераспределить для обработки данных.

Как только у вас есть struct заполнены (или array of structs), вы делаете эти данные доступными для вашего основного кода простым возвратом. Затем у вас есть один указатель на ваши данные, который вы можете легко передать вам в функцию печати или куда бы вам ни понадобились данные. Поскольку хранилище для ваших данных было выделено динамически, вы несете ответственность за освобождение используемой памяти, когда она больше не нужна.

Я привел примеры как функции print, так и free, чтобы проиллюстрировать передачу указателя на данные между функциями, а также практическую печать и освобождение памяти.

Разработка вашего кода подобным образом избавит вас от многих головных болей в долгосрочной перспективе. Есть много способов сделать это, пример ниже - просто один подход. Я прокомментировал код, чтобы помочь вам следовать. Посмотрите и дайте мне знать, если у вас есть вопросы.


Примечание: я заменил оригинал readQAs функция с версией, которую я первоначально написал, но имел давнюю проблему с. Когда используешь getline Вы должны сохранить начальный адрес для любого буфера, выделенного getline или повторяющиеся звонки getline приведет к segfault, когда getline пытается перераспределить свой буфер. В принципе, getline нужен способ отслеживания памяти, которую он использовал. Вы можете разделить буфер, выделенный getline до любого желаемого значения, если вы сохраняете начальный адрес первоначально выделенного буфера. Сохранение указателя на оригинал достаточно.

Это может быть особенно тонким, когда вы передаете буфер функциям, которые работают со строкой, таким как strtok или же strsep, Независимо от того, что не удалось сохранить начало буфера, выделенного getline приведет к segfault в любом цикле исчерпывается начальный 120-байтовый буфер, выделенный getline получение __memcpy_sse2 () from /lib64/libc.so.6 Если вы никогда не исчерпаете оригинальный 120-байтовый буфер, у вас никогда не возникнет ошибка. Итог, всегда сохраняйте начальный адрес для буфера, выделенного getline,

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

#define MAXQ 128
#define MAXA 16

typedef struct {
    char *q;
    char **ans;
    unsigned int nans;
} ques;

ques **readQAs (char *fn);
void prn_ques (ques **exam);
void free_ques (ques **exam);

int main (int argc, char **argv) {

    if (argc < 2) {
        fprintf (stderr,"\n error: insufficient input. Usage: %s <csvfile>\n\n", argv[0]);
        return 1;
    }

    ques **exam = NULL;     /* pointer to pointer to struct */

    /* allocate/fill exam structs with questions/answers    */
    if ( !( exam = readQAs (argv[1]) ) ) {
        fprintf (stderr, "\n error: reading questions/answers from '%s'\n\n", argv[1]);
        return 1;
    }

    prn_ques (exam);        /* print the questions/answers  */
    free_ques (exam);       /* free all memory allocated    */

    return 0;
}

/* allocate and fill array of structs with questions/answers    */
ques **readQAs (char *fn)
{
    FILE *fp = fopen (fn, "r");         /* open file and validate   */
    if (!fp) {
        fprintf (stderr,"\n error: Unable to open file '%s'\n\n", fn);
        return NULL;
    }

    char *line = NULL;      /* line buff, if NULL getline allocates */
    size_t n = 0;           /* max chars to read (0 - no limit)     */
    ssize_t nchr = 0;       /* num chars actually read by getline   */
    char *p = NULL;         /* general pointer to parse line        */
    char *sp = NULL;        /* second pointer to parse line         */
    char *lp = NULL;        /* line ptr (preserve line start addr)  */
    size_t qidx = 0;        /* index for questions structs          */
    size_t aidx = 0;        /* index for answers within structs     */

    ques **q = calloc (MAXQ, sizeof (*q));  /* allocate MAXQ ptrs   */
    if (!q) { fprintf (stderr,"\n Allocation error.\n\n"); return NULL; }

    /* for each line in file (fn)   */
    while ((nchr = getline (&line, &n, fp)) != -1)
    {
                                    /* test qidx = MAXQ-1, realloc  */
        aidx = 0;                   /* reset ans index each line    */

        lp = line;                  /* save line start address      */
        if (line[nchr - 1] == '\n') /* test/strip trailing newline  */
            line[--nchr] = 0;

        q [qidx] = calloc (1, sizeof (**q));    /* allocate struct  */
        q [qidx]-> ans = calloc (MAXA, sizeof (*(q[qidx]-> ans)));

        /* read question */
        *(p = strchr (line, ',')) = 0;  /* null-terminate ln at ',' */
        q [qidx]-> q = strdup (line);   /* alloc/read question      */
        sp = p + 1;                     /* sp now starts next ch    */

        /* read correct answer number */
        *(p = strrchr (sp, ',')) = 0;   /* null-term ln at last ',' */
        q [qidx]-> nans = *(p+1) - '0'; /* save num ans, cvt to %zd */

        /* read multi-choice answers */
        for (p = strtok (sp, ","); p && *p; p = strtok (NULL, ","))
            q [qidx]-> ans [aidx++] = strdup (p); /* alloc/read ans */

        line = lp;                      /* avoid __memcpy_sse2 err  */

        qidx++;                         /* inc index for next Q     */
    }
    if (line) free (line);              /* free line memory         */
    if (fp) fclose (fp);                /* close file stream        */

    return q;   /* return ptr to array of structs holding Q/A(s)    */
}

/* print formatted exam read from file */
void prn_ques (ques **exam)
{
    if (!exam) {
        fprintf (stderr, "\n %s() error: invalid exam pointer.\n\n", __func__);
        return;
    }

    size_t qidx = 0;        /* index for questions structs          */
    size_t aidx = 0;        /* index for answers within structs     */

    printf ("\nClass Exam\n\n");
    while (exam [qidx])
    {
        printf (" %2zd.  %s\n\n", qidx + 1, exam[qidx]-> q);
        aidx = 0;
        while (exam[qidx]->ans[aidx])
        {
            if (exam[qidx]-> nans == aidx + 1)
                printf ("\t(%c)  %-16s    (* correct)\n", (int)aidx + 'a', exam[qidx]->ans[aidx]);
            else
                printf ("\t(%c)  %s\n", (int)aidx + 'a', exam[qidx]->ans[aidx]);
            aidx++;
        }
        printf ("\n");
        qidx++;
    }
    printf ("\n");
}

/* free all memory allocated */
void free_ques (ques **exam)
{
    if (!exam) {
        fprintf (stderr, "\n %s() error: invalid exam pointer.\n\n", __func__);
        return;
    }

    size_t qidx = 0;        /* index for questions structs          */
    size_t aidx = 0;        /* index for answers within structs     */

    while (exam[qidx])
    {
        if (exam[qidx]->q) free (exam[qidx]->q);
        for (aidx = 0; aidx < MAXA; aidx++) {
            if (exam[qidx]->ans[aidx]) {
                free (exam[qidx]->ans[aidx]);
            }
        }
        free (exam[qidx]->ans);
        free (exam[qidx++]);
    }
    free (exam);
}

Выход / верификации:

$ ./bin/readcsvfile  dat/readcsvfile.csv

Class Exam

  1.  What function do you use to open a file?

        (a)  fscanf
        (b)  fclose
        (c)  fopen               (* correct)
        (d)  main

  2.  Which of the following is not a variable type?

        (a)  int
        (b)  float
        (c)  char
        (d)  string              (* correct)

  3.  How many bytes is a character?

        (a)  8
        (b)  4
        (c)  2
        (d)  1                   (* correct)

  4.  What programming language have you been studying this term?

        (a)  B
        (b)  A
        (c)  D
        (d)  C                   (* correct)

  5.  Which of the following is a comment?

        (a)  #comment
        (b)  //comment           (* correct)
        (c)  $comment
        (d)  %comment

  6.  Which of these is in the C Standard Library?

        (a)  stdio.h             (* correct)
        (b)  studio.h
        (c)  iostream
        (d)  diskio.h

  7.  What tool do we use to compile?

        (a)  compiler            (* correct)
        (b)  builder
        (c)  linker
        (d)  wrench

  8.  What function do you use to close a file?

        (a)  fscanf
        (b)  fclose              (* correct)
        (c)  fopen
        (d)  main

  9.  How do you include a file?

        (a)  #include            (* correct)
        (b)  //include
        (c)  $include
        (d)  %include

 10.  What are you doing this quiz on?

        (a)  paper
        (b)  whiteboard
        (c)  computer            (* correct)
        (d)  chalkboard

проверка Valgrind:

==16221==
==16221== HEAP SUMMARY:
==16221==     in use at exit: 0 bytes in 0 blocks
==16221==   total heap usage: 73 allocs, 73 frees, 3,892 bytes allocated
==16221==
==16221== All heap blocks were freed -- no leaks are possible
==16221==
==16221== For counts of detected and suppressed errors, rerun with: -v
==16221== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Другие вопросы по тегам