【问题标题】:Program won't read correctly from text file程序无法从文本文件中正确读取
【发布时间】:2018-03-21 21:07:55
【问题描述】:

好的,我知道代码可能很长,但我尽可能地减少了它。我希望代码能够正常工作,这样您就可以重新创建我的问题。我的问题是,当我尝试读取文本文件时,它并没有读取所有项目。好像只看了最后几篇。我可能在没有注意到的情况下不小心改变了一些东西,因为它以前工作得很好。当您在程序中并注册项目时,它会很好地计算项目。但是当您打开文件时,您刚刚保存了您的项目。它计算的项目数少于实际文件中的项目数,并且数组都出错了。如果有人能在我的代码中看到问题,那将不胜感激。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX 20

struct items
{
    int itemnumber;
    char name[30];
    int balance;
};

void open_file(FILE *enter_filename, char filename[], struct items aItems[], int *num_items)
{
    int i=0;

        printf("Choose filename (.txt).\n");
        scanf("%19s", filename);
        enter_filename=fopen(filename, "r+");
        if(enter_filename)
        {
            while(!feof(enter_filename))
            {
                 for(i = 0; i < *num_items; i++) 
                    {
                    fscanf(enter_filename, "Itemnumber: %d\n", &aItems[i].itemnumber);
                    fscanf(enter_filename, "Name: %s\n", aItems[i].name);
                    fscanf(enter_filename, "Balance: %d\n", &aItems[i].balance);
                    }
                if(!feof(enter_filename))
                {
                    *num_items=*num_items + 1;     
                }
            }
            printf("\nNumber of items: %d \n",*num_items);
            fclose(enter_filename);
        }
        else
        {
            printf("That file doesn't exist! Create a new one.\n");
            printf("What name do you want for your new file?\n");
            scanf("%19s", filename);
            enter_filename=fopen(filename, "w+");
            printf("File is created!\n");
            *num_items = 0;                 
            fclose(enter_filename);
        }
}
void register_item(struct items *aItems, int *num_items)
{
    int success=1;
    if(*num_items < MAX)
        {   
        while(1)
        {   
            printf("Item number:\n");                             
            scanf("%d", &aItems[*num_items].itemnumber);
            for(int i=0; i < *num_items; i++)
            {
            if(aItems[*num_items].itemnumber == aItems[i].itemnumber)
                {
                printf("Item number already exists, choose a unique item number.\n");
                success=0;
                break;
                }
            else
                {
                success=1;
                }
            }
        if(success)break;
        }
        printf("Name:\n");
        scanf("%29s", aItems[*num_items].name);
        strlwr(aItems[*num_items].name);
        printf("Balance:\n");
        scanf("%d", &aItems[*num_items].balance);   
        *num_items+=1;
        }
}
void print_item(struct items aItems[], int num_items)
{
    int i;
    for (i=0; i < num_items; i++)
    {
    printf("%d. Item number: %d Name: %s Balance: %d\n", i+1, aItems[i].itemnumber, aItems[i].name, aItems[i].balance);         
    }
}
void quit_program(char filename[], struct items aItems[], int *num_items)
{
    FILE *fil;
    fil=fopen(filename, "w+");                                             
    int i;
    for(i = 0; i < *num_items; i++) 
        {
        fprintf(fil, "Itemnumber: %d\n", aItems[i].itemnumber);
        fprintf(fil, "Name: %s\n", aItems[i].name);
        fprintf(fil, "Balance: %d\n\n", aItems[i].balance);
        }
    fclose(fil);
}   
int main(void)
{
    FILE *enter_filename;
    struct items aItems[MAX];

    int menu, num_items=0;
    char filename[20]; 

    open_file(enter_filename,filename, aItems, &num_items);

    while(menu!=3)
    {
        printf("\n");
        printf("1. Register new items to inventory.\n");
        printf("2. Print all items from inventory.\n");
        printf("3. Quit\n");
        scanf("%d", &menu);

        if(menu==1)
        {
           register_item(aItems, &num_items);
        }

        if(menu==2)
        {
          print_item(aItems, num_items); 
        }

        if(menu==3)
        {
         quit_program(filename, aItems, &num_items);  
        }
    }
return 0;
}

【问题讨论】:

  • 我不明白这怎么可能是minimal reproducible example 用于读取文件的问题。也许从头开始创建一个?一般来说,我推荐阅读How to debug small programs。
  • 参见Why is “while ( !feof (file) )” always wrong?。这是一个非常糟糕的模式。另外,请考虑显示它应该读取的文件的一部分。
  • 如果我从中删除更多代码或尝试从头开始制作,这将是一个不同的程序。我只添加了运行程序所需的东西。
  • 是的,但大部分代码都是关于交互式提示用户输入内容,这似乎与问题无关。另外,对于任何必须测试它以确定需要什么样的输入的人来说,这很烦人。对一些项目进行硬编码,将它们写出来,再次读入,然后打印出来。
  • FILE *enter_filename; ... open_file(enter_filename, ...);你将一个未初始化的FILE* 传递给你的函数。稍后,您再次将其传递给quit_program(filename...。这行不通。 FILE* 的命名 filename 非常可疑,

标签: c arrays file text struct


【解决方案1】:

在您的open_file 函数中,您使用while(!feof(enter_filename))。

这不是一种可靠的文件读取方式,as it is stated in this question. 对于您的情况,您最终的结果是,由于您在错误的 while(!feof(enter_filename)) 循环中设置了 num_items,因此您的 num_items 持有错误的值,并且它通过您的程序逻辑传播,因为您几乎在任何地方都在使用它,尤其是当再次写回文件,这解释了丢失的行。一旦您实现了here 或here 中提到的方法之一,请使用您的调试器确保num_items 与您的输入文件一致。

【讨论】:

    【解决方案2】:

    你的错误在这里:

            while(!feof(enter_filename))
            {
                 for(i = 0; i < *num_items; i++) 
                    {
                    fscanf(enter_filename, "Itemnumber: %d\n", &aItems[i].itemnumber);
                    fscanf(enter_filename, "Name: %s\n", aItems[i].name);
                    fscanf(enter_filename, "Balance: %d\n", &aItems[i].balance);
                    }
                if(!feof(enter_filename))
                {
                    *num_items=*num_items + 1;     
                }
            }
    

    在第一次通过循环时,*num_items 为零,因此不会输入执行实际读数的内部循环,aItems[i] 将未初始化。然后你增加项目计数器。

    在下一次传递中,将读取一个项目,即aItems[1],但它会接收第一个项目的数据。你增加计数器。在第三遍中,您读取了aItems[2],但立即将其覆盖,因为您的内部循环进行的遍数与数组中当前的元素一样多。显然,这是错误的。

    当您阅读文件时,您不知道有多少项目。因此,您必须阅读一个项目,测试它是否可以阅读,然后相应地增加计数器。通过fscanf的返回值测试文件是否结束或者输入是否正确,返回成功转换的项目数。

    你的循环可以这样工作:

            while (*num_items < MAX) {
                struct items *p = &aItems[*num_items];
    
                if (fscanf(f, "Itemnumber: %d\n", &p->itemnumber) < 1
                 || fscanf(f, "Name: %s\n", p->name) < 1
                 || fscanf(f, "Balance: %d\n", &p->balance) < 1) {
                    break;
                }
    
                (*num_items)++;
            }
    

    (我已将文件句柄称为f:enter_filename 太长且太误导。)

    其他注意事项:

    • 文件句柄和文件名是您的函数的本地文件;它们不在室外使用,并且手柄可以正确打开和关闭。因此,您不应将它们作为参数传递,而应将它们设为局部变量。
    • 变量menu 未初始化,启动程序时可能为3。

    【讨论】:

    • 你完美地解释了我的问题。你介意解释那个代码吗?我不太了解 if 语句中的条件。
    • fscanf 返回被转换的值的数量,或多或少有多少 % 格式字符串中的序列被理解。特殊值EOF 表示遇到文件结尾。 EOF 为负数,通常为 -1。如果无法读取任何数据行,则跳出循环。
    • (通过读取函数的返回值控制文件输入是读取文件的首选方式;请参阅 Serkan 答案中的链接。函数 feof() 和 ferror() 可以在 'EOF 之后使用` 发现原因是读取错误还是文件的实际结尾。)
    • 有趣。因此,while 循环既解决了我的问题,也解决了我对 while 循环的错误使用。非常感谢您的帮助。我学到了很多。
    猜你喜欢
    • 1970-01-01
    • 2015-01-22
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-30
    • 2018-07-24
    相关资源
    最近更新 更多