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
начальный размер для ожидаемого количества вопросов, (MAXQ
128
ниже), и выделить хранилище для этой суммы. Вы можете сделать то же самое для ожидаемых ответов на вопрос (MAXA
16
ниже). Если вы в конечном итоге читаете больше, чем каждый, вы можете легко перераспределить для обработки данных.
Как только у вас есть 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)