Как я могу прочитать известное количество строк неизвестного размера из файла .txt и сохранить каждую строку в строке матрицы (в C)? - PullRequest
0 голосов
/ 09 июня 2018

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

int main() {
  char str[MAXLEN], **fragmentsList;
  int number_of_strings, i, max, k;
  printf("Enter .txt file name: ");
  scanf("%s", str);
  printf("How many strings does the file has? ");
  scanf("%d", &number_of_strings);
  FILE *arq;
  arq = fopen(str, "r");
  for (i = 0, max = 0; !feof(arq); i++) {
    while (fscanf("%c") != '\n') {
      max++;
    }
    if (max > k) {
      k = max;
    }
  }
  fclose(arq);
  fragmentsList = malloc(k * sizeof(char));
  *fragmentsList = malloc(number_of_strings * sizeof(char));
  arq = fopen(str, "r");
  for (i = 0; !feof(arq); i++) {
    fscanf(arq, "%s", fragmentList[i]);
  }
  for (i = 0; i < number_of_strings; i++) {
    printf("%s", fragmentList[i]);
  }
  return 0;
}

1 Ответ

0 голосов
/ 10 июня 2018

Чтение неизвестного количества строк из файла в память в C является основной необходимостью.Есть несколько способов приблизиться к этому, но стандартная практика заключается в следующем:

  • объявить pointer to pointer to type (char** для строк в файле), чтобы позволить вам собирать иссылаться на каждую строку после чтения в память;

  • выделять некоторое разумно ожидаемое количество указателей для начала, чтобы избежать повторных обращений к realloc выделению указателей для каждой строки индивидуально (первоначально выделение 8, 16, 32, ..все работает нормально);

  • объявляем переменную для отслеживания количества прочитанных строк и приращения для каждой строки;

  • читаем каждую строкуфайл в буфер (POSIX getline работает особенно хорошо, потому что он сам будет динамически выделять достаточно памяти для обработки любой длины строки - освобождая вас от чтения с фиксированным буфером и выделяя и накапливая частичные строки до концастрока достигнута)

  • выделить хранилище для каждой строки, скопировать строку в новое хранилище и назначить начальные адресаs к вашему следующему указателю, strdup делает оба для вас, но, поскольку он выделяет, убедитесь, что вы подтвердите, что он успешен;

  • когда ваш индекс достигнет текущего количества выделенных указателей, *На 1031 * больше указателей (как правило, удваивая число или увеличивая число на 3/2 - скорость, если увеличение не особенно важно - важно то, чтобы у вас всегда был действительный указатель для назначения нового блока памятипридерживая свою линию);и

  • повторять до тех пор, пока файл не будет полностью прочитан.

При перераспределении памяти необходимо учитывать некоторые тонкости.Сначала никогда не realloc непосредственно на перераспределяемый указатель, например, не делайте:

mypointer = realloc (mypointer, current_size * 2);

, если realloc не удастся, он возвращает NULL, и если вы назначаете возврат к исходному указателю, вызамените адрес на ваши текущие данные с помощью NULL, что приведет к утечке памяти.Вместо этого всегда используйте временный указатель и проверяйте realloc успешно, прежде чем присваивать новый блок памяти исходному указателю, например,

    if (filled_pointers == allocated pointers) {
        void *tmp = realloc (mypointer, current_size * 2);

        if (tmp == NULL) {
            perror ("realloc-mypointer");
            break;      /* or use goto to jump out of your read loop,
                         * preserving access to your current data in
                         * the original pointer.
                         */
        }
        mypointer = tmp;
        current_size *= 2;
    }

Сложив части в примере, используя getline, вы можете сделатьчто-то вроде следующего.(примечание: код ожидает, что имя файла будет считано в качестве 1-го аргумента вашей программы, если аргумент не задан, программа будет читать из stdin по умолчанию)

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

#define NPTR 8      /* initial number of pointers (must be > 0) */

int main (int argc, char **argv) {

    size_t ndx = 0,             /* line index */
        nptrs = NPTR,           /* initial number of pointers */
        n = 0;                  /* line alloc size (0, getline decides) */
    ssize_t nchr = 0;           /* return (no. of chars read by getline) */
    char *line = NULL,          /* buffer to read each line */
        **lines = NULL;         /* pointer to pointer to each line */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* allocate/validate initial 'nptrs' pointers */
    if (!(lines = calloc (nptrs, sizeof *lines))) {
        perror ("calloc - lines");
        return 1;
    }

    /* read each line with POSIX getline */
    while ((nchr = getline (&line, &n, fp)) != -1) {
        if (nchr && line[nchr - 1] == '\n') /* check trailing '\n' */
            line[--nchr] = 0;               /* overwrite with nul-char */
        char *buf = strdup (line);          /* allocate/copy line */
        if (!buf) {             /* strdup allocates, so validate */
            perror ("strdup-line");
            break;
        }
        lines[ndx++] = buf;     /* assign start address for buf to lines */
        if (ndx == nptrs) {     /* if pointer limit reached, realloc */
            /* always realloc to temporary pointer, to validate success */
            void *tmp = realloc (lines, sizeof *lines * nptrs * 2);
            if (!tmp) {         /* if realloc fails, bail with lines intact */
                perror ("realloc - lines");
                break;          /* don't exit, lines holds current lines */
            }
            lines = tmp;        /* assign reallocted block to lines */
            /* zero all new memory (optional) */
            memset (lines + nptrs, 0, nptrs * sizeof *lines);
            nptrs *= 2;         /* increment number of allocated pointers */
        }
    }
    free (line);                    /* free memory allocated by getline */

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (size_t i = 0; i < ndx; i++) {
        printf ("line[%3zu] : %s\n", i, lines[i]);
        free (lines[i]);            /* free memory for each line */
    }
    free (lines);                   /* free pointers */

    return 0;
}

Просмотрите все и позвольте мнезнать, если у вас есть дополнительные вопросы.Если у вас нет getline или strdup, сообщите мне, и я буду рад помочь вам с реализацией, обеспечивающей их поведение.

...