读取空文件,输出为符号

时间:2016-12-18 05:48:32

标签: c file

我是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 个答案:

答案 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()之后使用dofflush()用于刷新输出流。 {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()从输入流中留下的任何字符。