首页 文章

读取空文件,输出为符号

提问于
浏览
-2

我是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

    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() 的返回值来控制循环:

    while(fscanf(fp, "%[^ ] %d %d\n",
                 dota[idx].name, &dota[idx].score, &dota[idx].num) == 3) {
        idx++;
    }
    

    如果通过调用 fscanf() 进行三次分配,则只会更新 player struct . 但是,这个简单解决方案的问题在于它不能很好地处理格式错误的输入 . 如果数据文件的某一行缺少某个字段,则 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() . 并且在 do 循环中的 scanf() 之后使用 fflush(stdin) . fflush() 用于刷新输出流 . fflush() 对输入流的行为在C标准(ISO / IEC 9899:2011 7.21.5.2/2)中明确未定义,但我认为Microsoft在此处偏离了标准 . 不过,您不应在便携式C代码中使用 fflush(stdin) . 相反,使用这样的东西:

    int c;
    
    ...
    
    scanf("%d", &choose);
    while ((c = getchar()) != '\n' && c != EOF)
        continue;                  // discard extra characters
    

    此代码从输入流中读取字符,直到达到 '\n'EOF ,清除前一次调用输入流中的 scanf() 留下的任何字符 .

相关问题