簡體   English   中英

如何將任何文件讀入鏈接列表?

[英]How can I read any file into a linked list?

我應該創建一個可以將任何文件讀入鏈接列表的程序。 這是我到目前為止想出的:

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

#define MAX_BUFFER_SIZE 1024

typedef struct list {
    char *string;
    struct list *next;
} LIST;

void print_list(LIST *head) {
    LIST *current = head;
    while (current != NULL) {
        printf("%s", current->string);
        current = current->next;
    }
}

void push(LIST **head, FILE **fp) {
    char line[MAX_BUFFER_SIZE];
    LIST *node, *current = *head;

    while(fgets(line, sizeof(line), *fp)) {
        node = malloc(sizeof(LIST));
        if (node == NULL) exit(1);

        node->string = strdup(line);
        node->next = NULL;

        if(current == NULL) {
            *head = node;
            current = node;
        } else {
            current->next = node;
            current = current->next;
        }
    }
}

int main(int argc, char *argv[]) {
    FILE *fp = fopen(argv[1], "r");
    LIST *head = NULL;

    push(&head, &fp);
    fclose(fp);
    print_list(head);
    return 0;
}

將鏈接列表的內容與輸入文件的內容進行比較時,這種比較在使用 .txt 文件時會成功,但在使用帶有二進制數據的文件時會失敗。 這表明我的程序更改了二進制文件的內容。

我究竟做錯了什么?

隨機二進制數據可以包含不可打印的字符。 或者可能包含零,這是字符串終止符,因此提前終止您的字符串。 只是不要將原始二進制數據作為字符串讀取和寫入或使用字符串函數,它不會像您期望的那樣工作。

如果您想讀取和寫入任何類型的任意數據,請改用freadfwrite ,並以二進制模式打開您的文件。

由於您使用的是 Linux,您可以使用 POSIX.1 getline()來讀取行,包括帶有嵌入 NUL 字節的行; 您確實需要使用fwrite()編寫這些行。

對於鏈表,您應該為fwrite()包含一個長度字段。 我還將使鏈表數據元素成為靈活的數組成員:

struct node {
    struct node *next;
    size_t       size;
    char         data[];
    /* Note: data[size+1], data[size] == '\0'.
             This is not necessary for correct operation,
             but allows one to assume there is always at
             least one char in data, and the data is followed
             by a nul byte. It makes further use of this
             structure easier. */
};

struct node *node_new(const char *data, size_t size)
{
    struct node *n;

    n = malloc(sizeof (struct node) + size + 1);
    if (!n) {
        fprintf(stderr, "node_new(): Out of memory.\n");
        exit(EXIT_FAILURE);
    }

    n->next = NULL;
    n->size = size;
    if (size > 0)
        memcpy(n->data, data, size);
    n->data[size] = '\0';

    return n;
}

閱讀行時,最容易將行添加到列表中:

struct node *list = NULL;
struct node *curr;

char   *line = NULL;
size_t  size = 0;
ssize_t len;

while (1) {
    len = getline(&line, &size, stdin);
    if (len < 0)
        break;

    curr = node_new(line, (size_t)len);

    curr->next = list;
    list = curr;
}

list = list_reverse(list);

完成后,您反轉列表,以獲取列表開頭的第一個讀取行:

struct node *list_reverse(struct node *curr)
{
    struct node *root = NULL;
    struct node *next;

    while (curr) {
        next = curr->next;

        curr->next = root;
        root = curr;

        curr = next;
    }

    return root;
}

要將每一行寫入流,例如使用fwrite(node->data, node->size, 1, stdout)

如果輸出流不是本地文件,而是管道或套接字,則fwrite()可以返回一個短計數。 這不是錯誤; 這僅意味着只能寫入部分數據。 為了滿足這些情況,您可以使用兩個輔助函數:一個確保所有數據都被寫入,即使在寫入管道時,另一個用於掃描列表,使用第一個輸出每一行:

static int fwriteall(const char *data, size_t size, FILE *out)
{
    size_t  n;

    while (size > 0) {
        n = fwrite(data, 1, size, out);
        if (n > 0) {
            data += n;
            size -= n;
        } else
            return -1; /* Error */
    }

    return 0; /* Success */
}

int list_writeall(FILE *out, struct node *list)
{
    for (; list != NULL; list = list->next)
        if (list->size > 0)
            if (fwriteall(list->data, list->size, out)
                return -1; /* Error */
    return 0; /* Success */
}

您可以使用fread()讀取某些預定義大小的塊,而不是getline() fread()

struct node *read_all(FILE *in, const size_t size)
{
    struct node *list = NULL;
    struct node *curr;
    size_t       used;

    while (1) {
        curr = malloc(sizeof (struct node) + size + 1);
        if (!curr) {
            fprintf(stderr, "read_all(): Out of memory.\n");
            exit(EXIT_FAILURE);
        }

        size = fread(curr->data, 1, size, in);
        if (used > 0) {
            /* Optional: Optimize memory use. */
            if (used != size) {
                void *temp;
                temp = realloc(curr, sizeof (struct node) + used + 1);
                /* Reallocation failure is not fatal. */
                if (temp) {
                    curr = temp;
                    curr->size = used;
                }
            }
        }
        curr->data[used] = '\0';

        curr->next = list;
        list = curr;
    }

    return list_reverse(list);
}

該函數返回反向列表(即,第一行在列表中)。 調用該函數后,您應該使用ferror(in)檢查是否讀取了整個輸入流,或者是否存在錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM