2015-10-20 112 views
0

讀取包含鏈接節點的二進制文件時出現問題。從二進制文件讀取/寫入鏈接的節點

這是代碼:

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; 

} 

執行的結果是:

堆棧LEN:100

數據以s和u是不一樣的:22145808 <> 22134800

正確的結果應該是:

所有的測試都通過

回答

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中包含的內存地址,這也不太可能是正確的。

您可以添加代碼來進行計數和寫入讀取功能。

+0

你能告訴我一個代碼應該如何的例子嗎? – jaalbertill

+0

你有寫作部分的代碼。這應該讓你朝着正確的方向前進。 –

1

您只能讀寫堆棧本身,而不是其節點的有效負載,它存儲在指針中。

節點本身沒有任何有意義的信息。或者跨會話有意義的信息,而是: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;  
} 
+0

你能告訴我一個代碼應該如何的例子嗎? – jaalbertill

+0

我已編輯帖子以添加一個工作示例。 –