Проблемы с чтением файла .txt и сохранением в массив - PullRequest
0 голосов
/ 16 февраля 2020

Мне дали файл .txt со специфической структурой c: в каждой строке есть строка с 5 символами, но со случайным количеством строк, и мы должны прочитать файл и сохранить его, как захотим. Я пытался сделать это с помощью связанного списка, и он работал просто отлично, но по мере увеличения размера файла время, которое требовалось для выполнения, было слишком длинным. С тех пор я пытался сохранить строки в массив строк, поэтому все будет храниться в памяти непрерывно. При выполнении я получаю ошибку ошибки сегментации, и я понятия не имею, почему. Код выглядит следующим образом:

int nLines (char *path)
{
    int answer = 0;
    FILE* fp;
    fp = fopen(path,"r");
    char line[6];

    while (fgets(line, sizeof(line),fp))
    {
        answer++;
    }

    return answer;
}

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

    char **storage = malloc(numberLines * 6 * sizeof(char));

    if(storage != NULL)
    {
        int i = 0;
        char line [6];

        while (fgets(line, sizeof(line),fp))
        {
            strcpy(storage[i], line);
            i++;
        }
    }

    free(storage);
}

Первая функция должна возвращать количество строк в файле. С помощью этой информации я пытаюсь выделить память, равную количеству строк * размеру каждой строки, так как я знаю заранее это значение. Я представляю себе, что проблема исходит из строки:

char **storage = malloc (numberLines * 6 *sizeof(char));

Я давно не трогал C, и я немного подзабыл с целыми указателями и воспоминаниями. Может кто-нибудь помочь, пожалуйста. Спасибо!

Ответы [ 2 ]

1 голос
/ 16 февраля 2020

ваше распределение неверно

int main (int argc, char *argv[])
{
    FILE* fp;
    fp = fopen(argv[1], "r");
    size_t numberLines = 0;

    char **storage = NULL; 

    char line [8];

    while (fgets(line, sizeof(line),fp))
    {
        storage = realloc(storage, (numberLines + 1) * sizeof(*storage));
        storage[numberLines] = malloc(8);
        strcpy(storage[numlines++], line);
    }
/* ... */
}

вам нужно выделить место для указателей, а затем место для строк. Это только демо, и вы должны реализовать правильную обработку ошибок (память и файл).

0 голосов
/ 17 февраля 2020

Если кто-то действительно хочет иметь онлайновый алгоритм, у него не будет доступного количества строк. Идиоматический c способ получить смежный динамический контейнер c состоит в перераспределении геометрически увеличивающейся емкости, например vector или ArrayList . C не имеет встроенного такого типа, но стоит того, чтобы его использовать, если его много использовать. Например, он читает от stdin до EOF и использует последовательность Фибоначчи в качестве емкости.

#include <stddef.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>

/** One line of maximum 5 `char` plus 1 `NUL`. */
struct Line { char str[6]; };

/** A dynamic line array. */
struct LineArray {
    struct Line *data; /* data -> (c0 < c1 || c0 == c1 == max_size) */
    size_t capacity, next_capacity; /* !data -> !size, data -> size<=capacity */
    size_t size;
};

/** Ensures `min_capacity` of `a`. Return success, otherwise, `errno` will be
 set: `realloc` or `ERANGE` -- tried allocating more then can fit in `size_t`
 or `realloc` doesn't follow [IEEE Std 1003.1-2001
 ](https://pubs.opengroup.org/onlinepubs/009695399/functions/realloc.html). */
static int reserve(struct LineArray *const a, const size_t min_capacity) {
    size_t c0, c1;
    struct Line *data;
    const size_t max_size = (size_t)-1 / sizeof(struct Line *);
    assert(a);
    if(!a->data) {
        if(!min_capacity) return 1;
        c0 = 8, c1 = 13;
    } else {
        if(min_capacity <= a->capacity) return 1;
        c0 = a->capacity, c1 = a->next_capacity;
    }
    if(min_capacity > max_size) return errno = ERANGE, 0;
    assert(c0 < c1); /* Fibonacci: c0 ^= c1, c1 ^= c0, c0 ^= c1, c1 += c0. */
    while(c0 < min_capacity) {
        size_t temp = c0 + c1; c0 = c1; c1 = temp;
        if(c1 > max_size || c1 < c0) c1 = max_size;
    }
    if(!(data = realloc(a->data, c0 * sizeof *a->data)))
        { if(!errno) errno = ERANGE; return 0; }
    a->data = data;
    a->capacity = c0;
    a->next_capacity = c1;
    return 1;
}

/** Adds one to the size of `a` and returns it (`push_back`.) Exceptional
 return null and `errno` is `realloc` or `ERANGE`. */
static struct Line *new_line(struct LineArray *const a) {
    assert(a);
    if(a->size >= (size_t)-1) { errno = ERANGE; return 0; } /* Unlikely. */
    if(!reserve(a, a->size + 1)) return 0; /* (Less) unlikely. */
    return a->data + a->size++;
}

/** Destructor. */
static void linearray_(struct LineArray *const a) {
    assert(a);
    free(a->data);
    a->data = 0, a->capacity = a->next_capacity = a->size = 0;
}

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

int main(void)
{
    struct LineArray storage = { 0, 0, 0, 0 };
    struct Line *s, *s_end;
    size_t l = 0, line_len;
    char line[7] = "";
    int success = EXIT_FAILURE;

    /* `line` must be capable of storing the "*[,5]\n\0". */
    assert(sizeof line == sizeof ((struct Line *)0)->str + 1);

    while (fgets(line, sizeof line, stdin))
    {
        l++;
        line_len = strlen(line);
        assert(line_len && line_len < sizeof line);
        /* Too long. */
        if(line[line_len - 1] != '\n') { errno = ERANGE; goto catch; }
        /* Cut off the trailing new-line. */
        line[line_len-- - 1] = '\0';
        /* Store `line`. */
        if(!(s = new_line(&storage))) goto catch;
        strcpy(s->str, line);
    }
    if(ferror(stdin)) goto catch;
    /* Print all. */
    for(s = storage.data, s_end = s + storage.size; s < s_end; s++)
        printf("stored: %s\n", s->str);
    success = EXIT_SUCCESS;
    goto finally;
catch:
    perror("Error");
    fprintf(stderr, "On line %lu: \"%s\".\n", (unsigned long)l, line);
finally:
    linearray_(&storage);
    return success;
}
...