简体   繁体   English

如何将任何文件读入链接列表?

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

I'm supposed to create a program that can read any file into a linked list.我应该创建一个可以将任何文件读入链接列表的程序。 This is what I came up with so far:这是我到目前为止想出的:

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

When comparing the contents of the linked list with the contents of the input file this comparison succeeds when using a .txt file but fails when using a file with binary data.将链接列表的内容与输入文件的内容进行比较时,这种比较在使用 .txt 文件时会成功,但在使用带有二进制数据的文件时会失败。 This suggests that my program changes the contents of the binary file.这表明我的程序更改了二进制文件的内容。

What am I doing wrong?我究竟做错了什么?

Random binary data can contain characters that are not printable.随机二进制数据可以包含不可打印的字符。 Or might contain zeroes, which is the string terminator and thus terminate your strings early.或者可能包含零,这是字符串终止符,因此提前终止您的字符串。 Just don't read and write raw binary data as strings or using string functions, it will simply not work as you expect.只是不要将原始二进制数据作为字符串读取和写入或使用字符串函数,它不会像您期望的那样工作。

If you want to read and write arbitrary data of any kind, use eg fread and fwrite instead, and open your files in binary mode.如果您想读取和写入任何类型的任意数据,请改用freadfwrite ,并以二进制模式打开您的文件。

Since you are using Linux, you can use POSIX.1 getline() to read lines, including lines with embedded NUL bytes;由于您使用的是 Linux,您可以使用 POSIX.1 getline()来读取行,包括带有嵌入 NUL 字节的行; you do need to write those lines using fwrite() .您确实需要使用fwrite()编写这些行。

For the linked list, you should include a length field for fwrite() .对于链表,您应该为fwrite()包含一个长度字段。 I'd also make the linked list data element a flexible array member:我还将使链表数据元素成为灵活的数组成员:

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

When reading lines, it is easiest to prepend the lines to the list:阅读行时,最容易将行添加到列表中:

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

When done, you reverse the list, to get the first read line at the beginning of the 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;
}

To write each line to a stream, you use for example fwrite(node->data, node->size, 1, stdout) .要将每一行写入流,例如使用fwrite(node->data, node->size, 1, stdout)

If the output stream is not a local file, but a pipe or socket, fwrite() can return a short count.如果输出流不是本地文件,而是管道或套接字,则fwrite()可以返回一个短计数。 It is not an error;这不是错误; it only means that only part of the data could be written.这仅意味着只能写入部分数据。 To cater for those cases, you can use two helper functions: one to ensure all of the data is written, even when writing to a pipe, and another to scan through the list, using the first one to output each line:为了满足这些情况,您可以使用两个辅助函数:一个确保所有数据都被写入,即使在写入管道时,另一个用于扫描列表,使用第一个输出每一行:

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 */
}

Instead of getline() , you can read chunks of some predefined size using fread() :您可以使用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);
}

The function returns the reversed list (ie, with first line first in list).该函数返回反向列表(即,第一行在列表中)。 After calling the function, you should check using ferror(in) whether the entire input stream was read, or if there was an error.调用该函数后,您应该使用ferror(in)检查是否读取了整个输入流,或者是否存在错误。

暂无
暂无

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

相关问题 如何创建 (C) 函数以使用“读取”将文件中的数据读取到链表中? - How can I create a (C) function to read data from a file into a linked-list using 'read'? 将文件传递到链表时,如何读取文件并确定其数据表示形式? - How can I read a file and determine its data representation as I pass it into a linked list? 如何创建从文件中读取的字符的链接列表? C语言 - How can I create a linked list of characters read from a file? C language 如何使用 strtok() 和 getline() 将文件的行读入链表? - How can I use strtok() and getline() to read the lines of a file into a linked list? 如何使用指针将矩阵文本文件读取到C中的链接列表中? - How can i read a matrix text file into a linked list in C using pointers? 在C语言中,我如何从文本文件中读取数据并将其插入一个链接列表,该链接列表根据名字按升序排列? - In C, how can i read data from a text file and insert them an linked list that organised in an ascending order according to the first names? 如何将字符串从文件复制到 C 中的链表? - how can i copy string from file to a linked list in C? 无法将文件中的数据读入链表 - Can't read data from file into a linked list 文件读\\写中的链表 - Linked list in file read\write 我不知道如何将输入文件中的字符串(单词)读入链表 - i cannot figure out how to read the strings(words) from an input file into a linked list
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM