Как прочитать любой файл в связанном списке и распечатать содержимое - PullRequest
0 голосов
/ 19 сентября 2018

Я новичок в программировании на 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;
}

Чтобы проверить, правильно ли моя программа прочитала содержимое файла, который я использую (после компиляции), оператор перенаправления '>'чтобы перенаправить вывод в файл, например:

./read inputFile > outputFile

После этого я использую cmp для сравнения 'inputFile' с 'outputFile'.

Когда я следую этому процессу для файла .txt, все работает нормально, и оба файла равны, но когда я использую

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

, чтобы создать файл со случайными двоичными данными и сравнить этот файлс выходным файлом оба файла не равны.

Так что, очевидно, моя программа изменяет содержимое двоичного файла, и если кто-то может указать на мои ошибки в моем коде, это будет очень цениться.

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 20 сентября 2018

Поскольку вы используете 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), был ли прочитан весь входной поток или произошла ошибка.

0 голосов
/ 19 сентября 2018

Случайные двоичные данные могут содержать символы, которые нельзя распечатать.Или может содержать нули, которые являются ограничителем строки и, таким образом, завершают ваши строки рано.Просто не читайте и не записывайте необработанные двоичные данные как строки или используя строковые функции, они просто не будут работать так, как вы ожидаете.

Если вы хотите читать и записывать произвольные данные любого типа, используйте, например, fread и fwrite и откройте ваши файлы в двоичном режиме.

...