我正在用 C 语言编写一个函数,该函数应该从文件中输入有关学生及其成绩的信息。然而,在运行程序时,测试有时会在 fscanf 处显示分析器错误,即使输出符合预期。
/*structure for grade*/
struct Grade {
char subject[51];
int grade;
};
/*structure for student*/
struct Student {
char name[21], surname[21];
int no_grades;
struct Grade grades[100];
};
int input_students(struct Student *students, int n) {
int i = 0, no, j, first;
char name[21], surname[21], subject[51];
FILE *database = fopen("input.txt", "r");
if (database == NULL) {
printf("Error while opening input.txt");
return 0;
}
while (/*i < n && */fscanf(database, "%20s %20s %50s %d\n", name, surname, subject,
&no) == 4) {
first = 1;
for (j = 0; j < i; j++) {
if (strcmp(students[j].name, name) == 0 &&
strcmp(students[j].surname, surname) == 0) {
first = 0;
if (students[j].no_grades >= 100) { /*limit for number of grades per student is 100*/
break;
}
strcpy(students[j].grades[students[j].no_grades].subject,
predmet);
students[j].grades[students[j].no_grades].grade = no;
students[j].no_grades++;
break;
}
}
if (first && i < n) {
strcpy(students[i].name, name);
strcpy(students[i].surname, surname);
students[i].no_grades = 0;
strcpy(students[i].grades[students[i].no_grades].subject, subject);
students[i].grades[students[i].no_grades].grade = no;
students[i].no_grades++;
i++;
}
}
/*while (fscanf(database, "%20s %20s %50s %d\n", name, surname, subject,
&no) == 4) {
for (j = 0; j < i; j++) {
if (strcmp(students[j].name, name) == 0 &&
strcmp(students[j].surname, surname) == 0) {
if (students[j].no_grades >= 100) {
break;
}
strcpy(students[j].grades[students[j].no_grades].subject,
subject);
students[j].grades[students[j].no_grades].grade = no;
students[j].no_grades++;
break;
}
}
}*/
fclose(database);
return i;
}
/更新,注释将代码更改为仅一个循环,同样的事情发生/
返回值是从文件中读取信息的学生数量,而i 分析器消息指示 fscanf 行中存在错误。 我似乎无法找到这是什么原因。我仔细检查了每个字符串是否以“\0”结尾,我已通过调试器运行该程序,它在应该的位置停止,我没有看到它遍历并使用未初始化的值,并且它采用的值符合预期. 我已经翻译了函数和变量的名称,因为它最初不是英文的,因此问题不可能出在函数名称与库函数名称重叠的地方,但我会在这里更改它。 测试代码。这是一个自动测试。 此测试的输出。 当测试代码在 main.c 中运行时,它不会显示任何编译器错误,调试器也不会显示段错误。 由此自动测试创建的 input.txt 示例。 这将是有关分析器的信息,记住我还只是一个学生==3928== Invalid write of size 1
==3928== at 0x37154582C7: _IO_vfscanf (in /lib64/libc-2.12.so)
==3928== by 0x371546465A: __isoc99_fscanf (in /lib64/libc-2.12.so)
==3928== by 0x400AD5: input_students (main.c:22)
==3928== by 0x401394: main (main.c:174)
==3928== Address 0x7feff2e00 expected vs actual:
==3928== Expected: stack array "name" of size 21 in frame 2 back from here
==3928== Actual: stack array "surname" of size 21 in frame 2 back from here
==3928== Actual: is 32 before Expected
int i, j, no_students;
struct Student students[10];
FILE* database = fopen("input.txt", "w");
fputs("Pero Peric Osnove_racunarstva 8", database);
fputc(10, database);
fputs("Suljo Suljic Osnove_racunarstva 9", database);
fputc(10, database);
fputs("Pero Peric Inzenjerska_matematika_1 6", database);
fclose(database);
no_students=input_students(students, 10);
printf("%d\n", no_students);
for (i=0; i<no_students; i++) {
printf("%s %s ", students[i].name, studenti[i].surname);
for (j=0; j<students[i].no_grades; j++)
printf("%s %d ", students[i].grades[j].subject, students[i].grades[j].grade);
printf("\n");
}
2
Pero Peric Osnove_racunarstva 8 Inzenjerska_matematika_1 6
Suljo Suljic Osnove_racunarstva 9
Pero Peric Osnove_racunarstva 8
Suljo Suljic Osnove_racunarstva 9
Pero Peric Inzenjerska_matematika_1 6
==17000== exp-sgcheck, a stack and global array overrun detector
==17000== NOTE: This is an Experimental-Class Valgrind Tool
==17000== Copyright (C) 2003-2012, and GNU GPL'd, by OpenWorks Ltd et al.
==17000== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==17000== Command: outputP9YqwH
最佳答案
好的,这就是我认为正在发生的事情:
no_students=input_students(students, 10);
所以您预计有 10 名学生,但实际上有 4 名。出于简单原因,我们将文件指针 database
指向行 0
(而不是地址 0) 。所以你有这个:
row name surname subject grade_cnt
0 Pero Peric Osnove_racunarstva 8\n
1 Suljo Suljic Osnove_racunarstva 9\n
3 Pero Peric Inzenjerska_matematika_1 6<END OF FILE>
注意:第 3 行末尾可能是文件末尾。
因此,您首先循环读取第 0、1 ... 3 行,然后尝试读取第 4 行。没有第 4 行,因此失败。但可能发生的情况是,当第一个循环完成时,数据库指向第 4 行,跳过 EOF 并读取超出文件缓冲区末尾的内容。
有两种测试方法:
- 第4行后输入空行
- 在
while()
循环之间关闭并重新打开文件。
您可能跳过文件指针的另一个原因是:
fscanf(database, "%20s %20s %50s %d\n", name, surname, subject, &no) == 4)
注意 \n
在 %d
之后,如果 \n
消失了会发生什么? fscanf()
正在寻找“\n”。
尝试在输入文件末尾添加空行。
或者删除“\n”。
fscanf(database, "%20s %20s %50s %d", name, surname, subject, &no) == 4)
这背后的另一个原因是数字,%20s
:
fscanf(database, "%s %s %s %d", name, surname, subject, &no) == 4)
如果我错了,请任何人在评论中告诉我。
由于 fscanf
读取 name
的 20 个字符。
编辑:现在我确信我是对的:
阅读 this 。 scanf
和 fscanf
应谨慎使用,最好的情况下应避免使用。
关于c - 由 fscanf 引起的 fscanf 分析器错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66188754/