在C中读写struct

时间:2010-05-11 16:29:56

标签: c file

我有一个结构:

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 *

` 我读了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);
}

4 个答案:

答案 0 :(得分:2)

您需要考虑以下几个观察到的问题:

(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 :(得分:1)

这对我来说很突出:

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()的返回值,并仅在feof()失败时检查fread()

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

在这个循环中:

    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的返回值。