从二进制文件读/写链接节点

时间:2015-10-20 15:53:16

标签: c struct linked-list

我在读取包含链接节点的二进制文件时遇到问题。

这是代码:

lib1.c

struct my_stack_node {
void *data;
struct my_stack_node *next;
};

struct my_stack {
int size;
struct my_stack_node *first;
};

int my_stack_write(struct my_stack *stack, char *filename){
int count = 0;
struct my_stack_node *aux;
FILE *file = fopen(filename, "wb");
if(stack->first != NULL){
    aux = stack->first;
    count++;
    while(aux->next != NULL){
        fwrite(&aux ,sizeof(aux), 1, file);
        aux = aux->next;
        count++;
    }
}
fwrite(&stack, sizeof(stack), 1, file); //Escriure stack
fclose(file);
return count;
}    

struct my_stack *my_stack_read(char *filename){
struct my_stack *stackRead;
struct my_stack_node *stackNode;
FILE *file = fopen(filename, "rb");

if(!file){
    puts("Impossible obrir el fitxer");
    return NULL;
}else{
    int primerInici = 0;

    while(!feof(file)){
        if(primerInici == 0){
            stackRead = (struct my_stack*) malloc(sizeof(struct my_stack));
            fread(stackRead, sizeof(stackRead), 1, file);
            primerInici = 1;
        }else{
            //Crear nou node i llegir-lo del fitxer
            stackNode = (struct my_stack_node*) malloc(sizeof(struct my_stack_node));
            fread(stackNode, sizeof(stackNode), 1, file);
            //Afegir node a la pila
            stackNode->next = stackRead->first;
            stackRead->first = stackNode;
        }
    }
    fclose(file);
    return stackRead;
}

}

的main.c

 struct my_data {
   int val;
   char name[60];
 };


int main() {
struct my_stack *s, *t, *u;
struct my_data *data, *data1, *data2;
//...more code
    u = my_stack_read("/tmp/my_stack.data");
if (! u) {
    puts("Error in my_stack_read (u)");
    exit(1);
}

if (my_stack_len(s) != my_stack_len(u)) {
    puts("Stacks s and u don't have the same len");
    exit(1);
}

// Test we can free the data and compare stacks s and u
while((data1 = my_stack_pop(s))) {
    data2 = my_stack_pop(u);
    if (! data2 || data1->val != data2->val || my_strcmp(data1->name, data2->name)) {
        printf("Data in s and u are not the same: %d <> %d\n", data1->val, data2->val);
        exit(1);
    }
    free(data1);
    free(data2);
}
//...more code 
puts("All tests passed");
return 0;

}

执行结果是:

Stack len:100

s和u中的数据不相同:22145808&lt;&gt; 22134800

正确的结果应该是:

所有测试均已通过

2 个答案:

答案 0 :(得分:1)

这就是问题(在my_stack_write中):

aux = stack->first;
count++;
while(aux->next != NULL){
    fwrite(&aux ,sizeof(aux), 1, file);
    aux = aux->next;
    count++;
}

您正在编写指针aux。不是aux指向的结构。 data指出的数据都不是重要的部分。

因此。想象一下你有这样的事情:

my_stack  { first=0x100 }
at memoryPosition 0x100 we have : my_stack_node { data=0x200; next=0x300 }
at memoryPosition 0x300 we have : my_stack_node { data=0x500; next=0x600 }
at memoryPosition 0x600 we have : my_stack_node { data=0x700; next=NULL }

对于该结构,您的程序正在写入:0x100,0x300
您正在写入构成链接列表的节点的内存地址。你错过了最后一个节点,这是一种不同的错误 但这没用。下次运行程序时,您的节点可能位于不同的内存地址中,因此保存它们没有意义。它是动态内存,每次运行程序时都可能驻留在不同的位置。

您应该写的是您的链表列出的数据。

在整个计划中重复同样的错误。

如何正确编写链接列表中包含的数据:

void writeStack(struct my_stack *stack, const char *filename)
{
  struct my_stack_node *aux;
  FILE *file = fopen(filename, "wb");
  if ( file==NULL )
  {
    fprintf( stderr, "Could not open %s for writting.\n", filename );
    exit(1);
  }
  if (stack != NULL)
  {
    aux = stack->first;
    while(aux != NULL)
    {
      //   aux->data is of type void*
      //   Assuming that aux->data contains a struct my_data
      //   Most likely it would be better to redefine data as having
      // type struct my_data*
      fwrite(aux->data ,sizeof(struct my_data), 1, file);
      aux = aux->next;
    }
  }
  fclose(file);
}    

这里我们遍历列表中的所有节点 对于每一个我们写入包含在其中的数据 请注意fwrite( aux->data,如何写出aux->data指向的数据,这是正确的 虽然fwrite( &aux,会写入aux中包含的内存地址,但这不太可能是正确的 并且fwrite( &aux->data,会写入aux->data中包含的内存地址,这也不太可能是正确的。

您可以添加用于计数和编写读取功能的代码。

答案 1 :(得分:1)

您只能读取和写入堆栈本身,而不是其节点的有效负载,它是通过void *指针存储的。

节点本身没有任何有意义的信息。或者跨会话有意义的信息,而不是:datanext指针仅在写入数据的会话中有效。

您的堆栈本质上是一个线性数据结构。而不是存储节点,将堆栈数据存储为data成员的数组。当您阅读它们时,使用新分配的节点和读取data字段构建列表。

您的堆栈使用void *指针来允许各种数据类型。因此,您必须找到一种方法来告诉读写方法应该如何写入或读取数据。

您可以提供一个回调函数,您可以在其中传递打开的文件。如果需要,这样的回调可以将复杂的数据结构作为有效负载处理。

编辑:下面的代码显示了如何使用自定义函数序列化堆栈以进行读取和写入的示例。对称回调应该将数据写入文件并读取数据。 read函数可以分配由堆栈拥有的内存。用户必须确保释放它。

回调可以返回一个负数来表示错误。要读取的堆栈不必为空。读数据只是被推到堆栈。

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

#define die(...) exit((printf(__VA_ARGS__), putchar('\n'), 1));

typedef struct Stack Stack;
typedef struct SNode SNode;

struct SNode {
    void *data;
    SNode *next;
};

struct Stack {
    SNode *head;
};

/*
 *      Core stack functions
 */
void stack_push(Stack *st, void *data)
{
    SNode *sn = malloc(sizeof(*sn));

    sn->data = data;
    sn->next = st->head;
    st->head = sn;
}

void *stack_pop(Stack *st)
{
    void *data;
    SNode *sn;

    if (st->head == NULL) die("Undeflow");

    sn = st->head;
    data = sn->data;
    st->head = sn->next;

    free(sn);
    return data;    
}

int stack_empty(const Stack *st)
{
    return (st->head == NULL);
}

/*
 *      Stack write function with custom callback
 */
int stack_write(const Stack *st, const char *filename,
    int (*func)(FILE *f, const void *data))
{
    const SNode *sn = st->head;
    size_t count = 0;

    FILE *f = fopen(filename, "wb");
    if (f == NULL) return -1;
    fwrite(&count, 1, sizeof(count), f);

    while (sn) {
        if (func(f, sn->data) < 0) {
            fclose(f);
            return -1;
        }
        count++;
        sn = sn->next;
    }

    fseek(f, SEEK_SET, 0);
    fwrite(&count, 1, sizeof(count), f);    
    fclose(f);

    return count;
}

/*
 *      Stack read function with custom callback
 */
int stack_read(Stack *st, const char *filename,
    int (*func)(FILE *f, void **data))
{
    size_t count = 0;
    size_t i;

    FILE *f = fopen(filename, "rb");
    if (f == NULL) return -1;
    fread(&count, 1, sizeof(count), f);

    for (i = 0; i < count; i++) {
        void *p;

        if (func(f, &p) < 0) {
            fclose(f);
            return -1;
        }

        stack_push(st, p);
    }

    fclose(f);
    return count;
}


/*
 *      Custom data struct with read/write functions
 */
struct my_data {
    int val;
    char name[60];
};

int my_data_write(FILE *f, const void *data)
{
    if (fwrite(data, sizeof(struct my_data), 1, f) < 1) return -1;
    return 0;
}

int my_data_read(FILE *f, void **data)
{
    *data = malloc(sizeof(struct my_data));

    if (*data == NULL) return -1;

    if (fread(*data, sizeof(struct my_data), 1, f) < 1) {
        free(data);
        return -1;
    }

    return 0;
}

/*
 *      Example client code
 */
int main()
{
    Stack s = {NULL};
    Stack t = {NULL};

    struct my_data aa = {23, "Alice Atkinson"};
    struct my_data bb = {37, "Bob Bates"};
    struct my_data cc = {28, "Carol Clark"};

    stack_push(&s, &aa);
    stack_push(&s, &bb);
    stack_push(&s, &cc);

    stack_write(&s, "kk", my_data_write);

    while (s.head) stack_pop(&s);

    stack_read(&t, "kk", my_data_read);

    while (t.head) {
        struct my_data *p = stack_pop(&t);

        printf("%4d '%s'\n", p->val, p->name);
        free(p);
    }

    return 0;    
}