在C中从文件读入结构

在C中从文件读入结构,c,C,我正在为大学做一个小项目(第一学期做一个书店实现),我有一个从文本文件读入结构列表的问题,其中有一个存储作者的二维字符数组。但是,它不能正常工作(每次启动程序时,它都会显示列表为空)。写入文件是有效的(我认为,因为它会用空数据覆盖我的文本文件) 示例数据: Adam Mickiewicz///Pan Tadeusz/Publisher 1/1833/24.99 Jules Verne///Around The World in 80 days/Publisher 1/1904/19.99 Jea

我正在为大学做一个小项目(第一学期做一个书店实现),我有一个从文本文件读入结构列表的问题,其中有一个存储作者的二维字符数组。但是,它不能正常工作(每次启动程序时,它都会显示列表为空)。写入文件是有效的(我认为,因为它会用空数据覆盖我的文本文件)

示例数据:

Adam Mickiewicz///Pan Tadeusz/Publisher 1/1833/24.99
Jules Verne///Around The World in 80 days/Publisher 1/1904/19.99
Jean-Jacques Sempe/Rene Goscinny//Little Nicholas/Publisher 2/1963/22.99
我的结构:

#define AK 3 // Constant denoting max number of authors

typedef struct
{
    char authors[AK][100];
    char title[255];
    char publisher[100];
    unsigned int year;
    double price;

    struct Book *next;
} Book;

Book *first; // Address of the first element in a list
从文件中读取:

void read_from_file(const char *path)
{
    int no_of_authors;
    int i; 
    printf("Loading...\n"); 
    FILE *fp = fopen(path, "r"); // Opening a file

    // Checking for errors
    if (!fp) {
        printf("Error reading from file!");
        return;
    }

    // The loading mechanism 
    no_of_authors = 0;
    while (!feof(fp)) {
        Book *new = (Book*) malloc(sizeof(Book));
        for (i = 0; i < AK; i++) {
            fscanf(fp, "%s/", new->authors[i]);
        }
        fscanf(fp, "%s/%s/%u/%lf", new->title, new->publisher,
                &new->year, &new->price);
        fscanf(fp, "\n");
        new = new->next;
    }

    fclose(fp);
    printf("Loading successful.");
}
void read_from_文件(const char*path)
{
int无作者;
int i;
printf(“加载…\n”);
FILE*fp=fopen(路径,“r”);//打开文件
//检查错误
如果(!fp){
printf(“读取文件时出错!”);
返回;
}
//加载机制
作者数量=0;
而(!feof(fp)){
Book*new=(Book*)malloc(sizeof(Book));
对于(i=0;i作者[i]);
}
fscanf(fp,“%s/%s/%u/%lf”,新建->标题,新建->发布者,
&新->年,&新->价格);
fscanf(fp,“\n”);
新建=新建->下一步;
}
fclose(fp);
printf(“加载成功”);
}
写入文件(以防万一):

void write_to_文件(const char*path,Book*first)
{
int i;
printf(“保存…\n”);
文件*fp=fopen(路径,“w”);
账面*当前=第一;
如果(!fp){
printf(“打开文件时出错!”);
dump_list(first);//转储列表以防止内存泄漏,这样可以正常工作
}
//储蓄机制
while(第一个!=NULL){
对于(i=0;i作者[i]);
}
fprintf(fp,“%s/%s/%u/%lf”,当前->标题,当前->发布者,
&当前->年度,&当前->价格);
fprintf(fp,“\n”);
}
fclose(fp);
printf(“保存成功”);
}

OP最大的失败是没有检查
fscanf()的返回值。
。如果代码这样做,问题将被更快地检测出来


在读取数据行时,首先要考虑的是:

输入可能有问题吗

对于学习者应用程序,这通常被认为是不正确的。输入要么“良好”,要么文件结束。我们不要假设数据的格式太好

事实证明,虽然数据文件可能没有错误,但读取它的代码可能是错误的。代码检查
*scanf()
返回值的第二个微妙原因-自我验证


对于面向行的数据,使用
fgets()
读取数据行比使用
feof()、fscanf()…
读取数据行要好得多,另请参见


使用
“%s”
读取不包含空格的文本。这也将读入
'/'
。由于
'/'
用于定界,
“%s”
不是可接受的输入说明符。像“亚当·米奇维茨”这样的名字还包括一个空格。不使用
“%s”
的第二个原因

考虑一下
fscanf(fp,“%s/”,new->authors[i])正在执行<代码>%s“
扫描到
新建->作者[i]
非空白字符。非空格字符后的字符是空格,而不是
'/'

使用
“%[^/]”
读取不包含
“/”
的文本

使用
“%n”
跟踪当前扫描偏移量

现在分析这行代码

    char *p = buf;
    Book *nu = malloc(sizeof *nu);  // no cast needed
    for (size_t i = 0; i < AK; i++) {
      int n = 0;
      sscanf(p, "%99[^/]/%n", nu->authors[i], &n);
      if (n == 0) {
        Handle_Parse_Error();
      }
      p += n; 
    }
    if (sscanf(p, "%254[^/]/%99[^/]/%u/%lf", 
        nu->title, nu->publisher, &nu->year, &nu->price) != 4) {
      Handle_Parse_Error();
    }

如果你用整数来计算价格,而不是用双精度(或类似的货币和次货币)来计算美元,事情就会变得简单起来。
new=new->next:你在哪里给
new->next
?struct Book{}赋值;与struct{}Book不同<代码>而(!feof(fp))
是错误的:它告诉您上次读取没有失败,但没有告诉您下次读取不会失败。
typedef struct{…}Book
定义匿名结构类型,并将其定义为type name
Book
<代码>结构书*下一步定义了一个指向类型
struct Book
的不透明指针,您从未定义过它(但这是可以的,因为它是一个指针,不需要知道大小)。一般来说,避免使用structs
typedef
,因为它在保存一些键入的同时,也会造成混乱(如本文所示)。
 char buf[sizeof(Book) * 2]; // Use an ample sized buffer
 while (fgets(buf, sizeof buf, fp)) {
    char *p = buf;
    Book *nu = malloc(sizeof *nu);  // no cast needed
    for (size_t i = 0; i < AK; i++) {
      int n = 0;
      sscanf(p, "%99[^/]/%n", nu->authors[i], &n);
      if (n == 0) {
        Handle_Parse_Error();
      }
      p += n; 
    }
    if (sscanf(p, "%254[^/]/%99[^/]/%u/%lf", 
        nu->title, nu->publisher, &nu->year, &nu->price) != 4) {
      Handle_Parse_Error();
    }
    if (nu->year < -6000 || nu->year > 2999) Fail_Year_Range();
Book *read_from_file(const char *path) {
  Book first;  // Only the .next member is used.
  first.next = NULL;
  Book *current = &first;

  // setup while () loop
  ...
  while (fgets(buf, sizeof bu, fp)) {
    ...
    Book *nu = malloc(sizeof *nu);
    nu->next = NULL;
    ...
    // parse data, fill in rest of nu->
    ....
    current->next = nu;  // update current pointer
    current = nu;
  }
  // close up file ...

  return first.next;
}