简体   繁体   English

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

[英]Read/write linked nodes from binary file

I have a problem reading a binary file which contains linked nodes. 我在读取包含链接节点的二进制文件时遇到问题。

This is the code: 这是代码:

lib1.c 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 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;

}

The result of the execution is: 执行的结果是:

Stack len: 100 堆栈长度:100

Data in s and u are not the same: 22145808 <> 22134800 s和u中的数据不相同:22145808 <> 22134800

The correct result should be: 正确的结果应该是:

All tests passed 所有测试通过

Here lies the problem (inside my_stack_write) : 这是问题所在(在my_stack_write内部):

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

You are writting the pointer aux . 您正在编写指针aux Not the struct which is being pointed by aux . 不是aux所指向的结构。 Neither the data pointed by data , which is the important part. 数据都没有指向data ,这是很重要的部分。

So. 所以。 Imagine you have something like this : 假设您有这样的事情:

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 }

For that structure your program is writting : 0x100, 0x300 对于该结构,您的程序正在编写:0x100、0x300
You are writting the memory addresses of the nodes making up your linked list. 您正在写组成链接列表的节点的内存地址。 And you are missing the last node, which is a different kind of error. 而且您缺少最后一个节点,这是另一种错误。
But that is useless. 但这是没有用的。 Next time you run your program your nodes may be in different memory addresses so there is no point in saving them. 下次运行程序时,节点可能位于不同的内存地址中,因此没有必要保存它们。 It is dynamic memory, it may reside at different places each time you run your program. 它是动态内存,每次运行程序时它可能位于不同的位置。

What you should be writting instead is the data your linked list is listing. 您应该写的是链表列出的数据。

This same mistake is repeated in pretty much the whole program. 在整个程序中几乎都会重复同样的错误。

How to properly write the data contained in the linked list : 如何正确写入链表中包含的数据:

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);
}    

Here we traverse all the nodes in the list. 在这里,我们遍历列表中的所有节点。
And for each we write the data in them contained. 对于每个,我们将其中的数据写入其中。
Notice how fwrite( aux->data, writes the data pointed at by aux->data , which is correct. 请注意fwrite( aux->data,如何写入fwrite( aux->data,指向的aux->data ,这是正确的。
While fwrite( &aux, would write the memory address contained at aux , which is unlikely to be correct. fwrite( &aux,将写入aux包含的内存地址,这不太可能是正确的。
And fwrite( &aux->data, would write the memory address contained at aux->data , which is also unlikely to be correct. fwrite( &aux->data,将写入aux->data包含的内存地址,这也不大可能是正确的。

It is up to you to add code for counting and to write the reading function. 您可以自行添加用于计数的代码并编写读取功能。

You only read and write the stack itself, not the payload of its nodes, which is stored voa a void * pointer. 您只读取和写入堆栈本身,而不读取堆栈节点的有效负载,堆栈是通过void *指针存储的。

The nodes themselves carry no meaningful information. 节点本身不携带有意义的信息。 Or information that is meaningful across sessions, rather: The data and next pointers are valid only in the session that writes the data. 或对整个会话有意义的信息: datanext指针仅在写入数据的会话中有效。

Your stack is essentially a linear data structure. 您的堆栈实质上是线性数据结构。 Instead of storing the nodes, store the stack data as array of data members. 代替存储节点,将堆栈数据存储为data成员数组。 When you read them in, construct a list with freshly allocated nodes and the read data fields. 读入它们时,请构造一个包含新分配的节点和读取data字段的列表。

Your stack uses void * pointers to allow for various data types. 您的堆栈使用void *指针来允许各种数据类型。 You must therefore find a way to tell the read and write methods how the data should be written or read. 因此,您必须找到一种方法来告诉读写方法应如何写入或读取数据。

You could provide a callback function where you pass the opened file. 您可以在传递打开的文件的地方提供回调函数。 Such callbacks could deal with complex data structures as payload, if needed. 如果需要,此类回调可以将复杂的数据结构作为有效载荷处理。

Edit : The code below shows an example of how to serialise a stack with custom functions for reading and writng. 编辑 :下面的代码显示了一个如何使用自定义函数读取和写入序列化堆栈的示例。 The symmetric callbacks should write the data to the file and read the data. 对称回调应将数据写入文件并读取数据。 The read function can allocate memory, which is owned by the stack. 读取功能可以分配由堆栈拥有的内存。 The user must make sure to free it. 用户必须确保将其释放。

The callbacks can return a negative number to indicate an error. 回调可以返回负数以指示错误。 The stack to read need not be empty. 要读取的堆栈不必为空。 Read data ist just pushed to the stack. 读取数据ist刚刚被压入堆栈。

#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;    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM