如何读取链表中的任何文件并打印内容

时间:2018-09-19 18:02:10

标签: c linux pointers linked-list binaryfiles

我是C编程的新手,应该创建一个程序,该程序可以将任何文件读入链表,然后将链表的内容打印到屏幕上。到目前为止,这是我想出的:

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

要测试我的程序是否正确读取了我使用的文件内容(编译后),redirection operator'>'将输出重定向到文件,例如:

./read inputFile > outputFile

此后,我使用cmp比较“ inputFile”和“ outputFile”。

当我对.txt文件执行此过程时,一切正常,并且两个文件都相等,但是当我使用

dd if=/dev/urandom of=inputFile count=20000003 bs=1  

使用随机二进制数据创建文件,并将此文件与输出文件进行比较,两个文件不相等。

显然,我的程序更改了二进制文件的内容,如果有人指出我的代码错误,将不胜感激。

提前谢谢

2 个答案:

答案 0 :(得分:2)

随机二进制数据可以包含不可打印的字符。或者可能包含零,这是字符串终止符,因此可以尽早终止字符串。只是不要将原始二进制数据读取或写入为字符串或使用字符串函数,否则将无法按预期工作。

如果您想读写任何类型的任意数据,请使用例如改为freadfwrite,然后以二进制模式打开文件。

答案 1 :(得分:0)

由于使用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 */
}

您可以使用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)检查是否已读取整个输入流,或者是否有错误。