我尝试用C语言编写一个程序,从input.txt文件中提取数据,并按学生ID的升序将其插入record.txt文件中。
输入.txt的内容:
1
2012 Bob CS21
1999 Teddy CS35
2
3
2001 Eric CS11
2011 CS12 CS87
记录.txt的内容:
1287 Nancy CS11
1865 Brown CS33
当我运行程序时,input.txt中的以下数据应该被插入到record.文件中(包含学生ID、姓名和课程的有效数据):
2012 Bob CS21
1999 Teddy CS35
2001 Eric CS11
然后插入record.txt文件后的内容(升序):
1287 Nancy CS11
1865 Brown CS33
1999 Teddy CS35
2001 Eric CS11
2012 Bob CS21
下面是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
typedef struct {
int id;
char name[20];
char course[5];
} Student;
int read_records(Student s[]) {
FILE *fp;
int i=0;
fp = fopen("record.txt", "r");
if (fp == NULL) {
printf("File doesn't exist\n");
return 0;
}
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
fclose(fp);
return i;
}
void write_records(Student s[], int n) {
FILE *fp;
fp = fopen("record.txt", "w");
if (fp == NULL) {
printf("File doesn't exist\n");
return;
}
for (int i=0; i<n; i++) {
fprintf(fp, "%d %s %s\n", s[i].id, s[i].name, s[i].course);
}
fclose(fp);
}
void insert_records(Student s[], int n) {
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL) {
printf("File doesn't exist\n");
return;
}
int i=n;
while (!feof(fp)) {
fscanf(fp, "%d %s %s\n", &s[i].id, s[i].name, s[i].course);
i++;
}
fclose(fp);
write_records(s, i);
printf("Insertion is done.\n");
}
void display_records(Student s[], int n) {
for (int i=0; i<n; i++) {
printf("%d %s %s\n", s[i].id, s[i].name, s[i].course);
}
}
int main() {
int n;
Student s[MAX];
n = read_records(s);
int opt;
while (1) {
printf("1. Insert\n");
printf("2. Display\n");
printf("3. Exit\n");
printf("Choose an option: ");
scanf("%d", &opt);
switch(opt) {
case 1: insert_records(s, n);
break;
case 2: display_records(s, n);
break;
case 3: exit(0);
}
}
}
当我运行程序时,将要求用户选择一个选项。
如果我输入2,它将显示record.txt的内容。正如我所期望的,它运行得很好。
如果我输入1,应该执行插入,并打印“插入完成”。但是,程序并没有像我预期的那样工作。它只是什么也不显示。
我对此感到困惑,我想知道如何修复程序。
1条答案
按热度按时间mpbci0fu1#
此循环
因为当
fscanf
遇到无法转换的输入时,该输入会留在流中。该文件永远不会耗尽。用文件
第一个
fscanf
调用将转换1 "2012" "Bob"
。第二个调用将无法将CS21
转换为整数。此外,请参阅Why is “while( !feof(file) )” always wrong?。
另请注意,
*scanf
函数中的无界%s
说明符是dangerous asgets
。使用field-width specifier来限制可以读入缓冲区的数据量。这应该是允许的最大 * 字符串长度 *,并且最多应该是缓冲区的大小减1,为空终止字节留出空间。(即char s[256]; scanf("%255s", s);
)。解决方案是永远不要忽略
fscanf
系列函数的返回值。使用这些返回值来确定程序应该如何进行。如果fscanf
无法匹配预期的 * 转换 * 数,则必须准备好清理输入流。这通常意味着消耗字符,直到找到行尾。然而,这说起来容易做起来难。如上所述,
%s
跳过空格(包括换行符)的方式意味着它可能会在寻找有效字符时越界。一般的建议是避免使用
scanf/fscanf
,而是使用fgets
读取整行,然后使用sscanf
(或其他工具)解析这些行。实际情况如下:
对于
input.txt
文件,将打印:一旦您知道
sscanf
成功解析了预期的转换次数,您就可以继续验证记录(例如,根据您的示例,“CS12” 是一个无效名称,您可能需要一种方法来避免重复条目)。在代码中,只有在确保遵循了所有这些步骤之后,才应该递增
i
。在获得合并的记录列表之后,应该使用
qsort
对数组进行排序,比较函数如下然后再写出记录。
i
最终将溢出,调用Undefined Behaviour。在此点之后的程序结果通常无法推断。