【问题标题】:Read and write struct in C在 C 中读取和写入结构
【发布时间】:2010-05-11 16:29:55
【问题描述】:

我有一个结构:

typedef struct student
{
        char fname[30];
        char sname[30];
        char tname[30];
        Faculty fac;
        int course;
        char group[10];
        int room;
        int bad;
} Student;

我从文件中读取它:

Database * dbOpen(char *fname)
{
        FILE *fp = fopen(fname, "rb");
        List *lst, *temp;
        Student *std;
        Database *db = malloc(sizeof(*db));

        if (!fp)
                return NULL;

        FileNameS = fname;

        std = malloc(sizeof(*std));
        if (!fread(std, sizeof(*std), 1, fp)) {
                db->head = db->tail = NULL;
                return db;
        }

        lst = malloc(sizeof(*lst));
        lst->s = std;
        lst->prev = NULL;
        db->head = lst;
        while (!feof(fp)) {
                fread(std, sizeof(*std), 1, fp); 
                temp = malloc(sizeof(*temp));
                temp->s = std;
                temp->prev = lst;
                lst->next = temp;
                lst = temp;
        }
        lst->next = NULL;
        db->tail = lst;

        fclose(fp);

        return db;
}

我有一个问题......在最后一条记录中,我有一个这样的文件指针: `FP 0x10311448 {_ptr = 0x00344b90“НННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННН_ _iobuf * P>

` 我把最后一条记录读了 2 遍...

保存文件代码:

void * dbClose(Database *db)
{
        FILE *fp = fopen(FileNameS, "w+b");
        List *lst, *temp;

        lst = db->head;
        while(lst != NULL) {
                fwrite(lst->s, sizeof(*(lst->s)), 1, fp);
                temp = lst;
                lst = lst->next;
                free(temp);
        }
        free(db);
        fclose(fp);
}

【问题讨论】:

  • 这是我喜欢 STL 容器的众多原因之一。很容易忘记调试列表代码是多么有趣。
  • 您遇到的问题描述不清楚。你能详细说明一下吗?另外,我相信最后一段代码是重复的。

标签: c file


【解决方案1】:

几个观察到的和表面上的问题供您考虑:

(1) 如果文件的字节长度不是sizeof(Student) 的倍数怎么办?然后这段代码:

while (!feof(fp)) {
    fread(std, sizeof(*std), 1, fp);
    ...

可以将部分结构读取到std 指向的内存中。 std 的内容将被部分填充,并可能在内存中留下未终止的 C 字符串。

你必须检查fread()的返回值。

(2) 这是一个更重要的问题。尽管您将指针存储在链表的每个元素中,但您正在重新使用分配并指向 std 的内存。

您的意图似乎是为每个 Student 记录分配新内存。在这种情况下,您可以在从文件中读取多条记录的循环体中调用 malloc()

这个错误取决于文件长度,而且——文件必须包含多个记录。

(3) 您可以重新组织您的代码,以便在循环迭代期间也读取第一条记录,以删除不必要的重复代码。

(4) 您可以考虑使用calloc() 来确保您已经初始化了Student 的所有记录。

像这样:

Database * dbOpen(char *fname)
{
    FILE *fp = fopen(fname, "rb");
    List *lst, *temp;
    Student *std;
    Database *db = NULL;

    if (!fp)
            return db;

    FileNameS = fname;

    db = malloc(sizeof(*db));
    db->head = NULL;
    lst = NULL;
    while (!feof(fp)) {
            std = malloc(sizeof(*std));
            if (!fread(std, sizeof(*std), 1, fp))
            {
                free(std);
                fprintf(stderr, "Input file concludes in partial record.\n");
                break;
            }

            temp = malloc(sizeof(*temp));
            temp->s = std;
            temp->prev = lst;
            temp->next = NULL;

            if (lst == NULL)
                db->head = temp;
            else
                lst->next = temp;
            lst = temp;
    }
    assert(lst->next == NULL ); /* Now performed above by temp->next assignement. */
    db->tail = lst;

    fclose(fp);

    return db;
}

我没有编译和测试上面的代码,但它应该接近工作了。注意如何添加一个特殊情况来初始化db->head(第一次通过循环,lst 等于 NULL),否则之前的列表元素链接到新的添加元素(以后的迭代)。当然,我们还应该检查来自malloc() 的返回值,但为了清楚起见,这里省略了。

【讨论】:

  • 第 1 点展示了为什么 while (feof(...)) 通常是错误的典型示例。在这种情况下,它应该只是 while (fread(...) == 1)
【解决方案2】:

这对我来说很突出:

while (!feof(fp)) { 
            fread(std, sizeof(*std), 1, fp);  
            temp = malloc(sizeof(*temp)); 
            temp->s = std; 
            temp->prev = lst; 
            lst->next = temp; 
            lst = temp; 
        } 

您不应该使用feof() 作为循环条件;直到您尝试读取文件末尾之后才会设置文件结束指示符,因此您的循环将执行太多次。重构循环以使用fread() 的返回值,并仅在fread() 失败时检查feof()

while (fread(std, sizeof *std, 1, fp) == 1)
{
  temp = malloc(sizeof *temp);
  temp->s = std;
  ...
}
if (feof(fp)) 
  // handle end of file
else
  // handle other read error

【讨论】:

    【解决方案3】:

    第一次没有深入挖掘,我会说你已经写了你记录的结尾。您显示的输出看起来很像调试内存输出,您会看到它尾随 malloc 缓冲区。也许您的数据库没有完全正确对齐?现在再看一些...

    【讨论】:

      【解决方案4】:

      在这个循环中:

          while (!feof(fp)) {
                  fread(std, sizeof(*std), 1, fp); 
                  temp = malloc(sizeof(*temp));
                  temp->s = std;
                  temp->prev = lst;
                  lst->next = temp;
                  lst = temp;
          }
      

      您正在使用fread 的结果,而没有检查它是否成功返回。 在假设成功读取数据之前,您应该检查feof(fp)fread 的返回值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-10
        • 1970-01-01
        • 1970-01-01
        • 2013-08-02
        • 1970-01-01
        • 2016-09-04
        • 2013-08-24
        • 1970-01-01
        相关资源
        最近更新 更多