我是c的新人。所以在我的大学,我只是在c学习文件。我有一个任务。如果我在我的项目目录中放入一个空文件,并阅读它。输出是符号(我不知道它是什么符号)。所以这是代码,请帮助
player dota[100];
FILE *fp;
fp = fopen("soal09.txt", "r");
if(fp == NULL)
{
printf("Error Opening The File!!\n");
return 0;
}
else
{
while(!feof(fp))
{
fscanf(fp, "%[^ ] %d %d\n", &dota[idx].name, &dota[idx].score, &dota[idx].num);
idx++;
}
}
fclose(fp);
do
{
enter();
menu();
printf("Input your choice [1..5]: ");
scanf("%d", &choose); fflush(stdin);
if(choose == 1)
{
system("cls");
enter();
printf("%-20s %-15s %s\n", "Player Name", ": Average Score", ": Number of Playing");
printf("====================================================================\n");
for(int i = 0; i < idx; i++)
{
printf("%-20s %-15d %d\n", dota[i].name, dota[i].score, dota[i].num);
}
printf("\nPress Enter to Continue...");
getchar();
}
getchar();
return 0;
}
,输出为╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠-858993460 谢谢^^
答案 0 :(得分:0)
feof()
检查的文件结束指示符仅在先前的文件I / O操作失败后设置。您必须尝试I / O操作才能确定是否已到达文件末尾。因此,对于空文件,您的代码会尝试读取文件,文件结束指示符已设置,没有值读入第一个struct
,但idx
会递增,因此它看起来很像就像你为玩家添加了数据一样。但是第一个struct
的字段是未初始化的,所以你看到了垃圾。另请注意,dota[idx].name
可能是char
s的数组,因此当传递给char
时,它会衰减到指向fscanf()
的指针。使用&dota[idx].name
就像你一样,虽然看似有效,但却是错误的。它确实会导致编译器发出警告,你应该启用它们(我总是至少使用gcc -Wall -Wextra -pedantic
)。
You should not use feof()
to control file I/O loops。一个简单的解决方案是使用fscanf()
的返回值来控制循环:
while(fscanf(fp, "%[^ ] %d %d\n",
dota[idx].name, &dota[idx].score, &dota[idx].num) == 3) {
idx++;
}
如果通过调用player
进行了三次分配,则只会更新struct
fscanf()
。但是,这个简单解决方案的问题在于它不能很好地处理格式错误的输入。如果数据文件的某一行缺少字段,则struct
将被错误地填充,并且循环将终止,即使文件中有更多行要读取。此外,由于没有为字符串转换指定字段宽度,因此长名称可能会导致程序崩溃。
更好的解决方案是使用fgets()
将文件行读入缓冲区,然后使用sscanf()
从缓冲区中提取信息:
#include <string.h> // for strcpy()
...
char buffer[1000];
int line = 0;
char temp_name[100];
int temp_score, temp_num;
while(fgets(buffer, sizeof(buffer), fp)) {
++line;
if (sscanf(buffer, "%99[^ ] %d %d\n",
temp_name, &temp_score, &temp_num) == 3) {
strcpy(dota[idx].name, temp_name);
dota[idx].score = temp_score;
dota[idx].num = temp_num;
++idx;
} else if (buffer[0] != '\n') {
fprintf(stderr, "Bad input in line %d\n", line);
}
}
这里,声明一个慷慨的buffer
接受文件中的一行文本,并声明临时变量来保存要存储在struct
字段中的值。我为temp_name
选择了100的大小,但这应该与实际struct
中字符串数组的大小相匹配。请注意,sscanf()
中的字符串转换的字段宽度为99,因此最多99个非空格(非非空格,以及为什么不在此处使用%99s
?)字符匹配,为'\0'
添加空间。
fgets()
到达文件末尾时,它将返回NULL
,因此循环将继续,直到发生这种情况。对于每个读取的行,line
计数器会递增。然后sscanf()
用于将数据读入临时变量。检查从sscanf()
返回的值以确保进行了3次分配,如果是,则将数据复制到struct
,并且idx
递增。请注意,需要strcpy()
才能将字符串从temp_name
复制到dota[idx].name
。
如果从sscanf()
返回的值表示发出了3个以外的分配,则会检查buffer
是否包含空行。如果没有,则会向stderr
打印一条错误消息,提供文件中错误输入的行号。
进一步评论。您的do
循环似乎缺少关联的while()
。并且在fflush(stdin)
循环内的scanf()
之后使用do
。 fflush()
用于刷新输出流。 {C}标准(ISO / IEC 9899:2011 7.21.5.2/2)中明确定义了fflush()
对输入流的行为,但我认为Microsoft在此处偏离了标准。不过,您不应在便携式C代码中使用fflush(stdin)
。相反,使用这样的东西:
int c;
...
scanf("%d", &choose);
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
此代码从输入流中读取字符,直到达到'\n'
或EOF
,清除前一次调用scanf()
从输入流中留下的任何字符。