我是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谢谢^^
1 回答
由
feof()
检查的文件结束指示符仅在先前的文件I / O操作失败后设置 . 您必须尝试I / O操作才能确定是否已到达文件末尾 . 因此,对于空文件,您的代码会尝试读取文件,文件结束指示符已设置,第一个struct
中没有读取任何值,但idx
会递增,因此看起来您已为播放器添加了数据 . 但是第一个struct
的字段是未初始化的,所以你看到了垃圾 . 另请注意,dota[idx].name
可能是char
的数组,因此当传递给fscanf()
时,它会衰减到指向char
的指针 . 使用&dota[idx].name
,就像你一样,虽然看起来有效,但是错了 . 它确实会导致编译器发出警告,你应该启用它们(我总是使用至少gcc -Wall -Wextra -pedantic
) .You should not use feof() to control file I/O loops . 一个简单的解决方案是使用
fscanf()
的返回值来控制循环:如果通过调用
fscanf()
进行三次分配,则只会更新player
struct
. 但是,这个简单解决方案的问题在于它不能很好地处理格式错误的输入 . 如果数据文件的某一行缺少某个字段,则struct
将被错误地填充,并且循环将终止,即使文件中有更多行要读取 . 此外,由于没有为字符串转换指定字段宽度,因此长名称可能会导致程序崩溃 .更好的解决方案是使用
fgets()
将文件行读入缓冲区,然后使用sscanf()
从缓冲区中提取信息:这里,声明了一个慷慨的
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()
. 并且在do
循环中的scanf()
之后使用fflush(stdin)
.fflush()
用于刷新输出流 .fflush()
对输入流的行为在C标准(ISO / IEC 9899:2011 7.21.5.2/2)中明确未定义,但我认为Microsoft在此处偏离了标准 . 不过,您不应在便携式C代码中使用fflush(stdin)
. 相反,使用这样的东西:此代码从输入流中读取字符,直到达到
'\n'
或EOF
,清除前一次调用输入流中的scanf()
留下的任何字符 .