Нет ничего сложного в том, что вы читаете неизвестное количество двойных значений из файла или stdin
и сохраняете их в моделированном 2D-массиве. (pointer-to-pointer-to-type) Поскольку вы должны предположить, что количество столбцов также может различаться для каждой строки, вам нужно аналогичным образом распределить хранилище столбцов, отслеживать количество значений, выделенных/прочитанных, и способ перераспределения хранилища столбцов, если/когда достигнуто максимальное количество столбцов. Это позволяет обрабатывать массив без зубьев так же легко, как массив с фиксированным размером столбцов.
Существует один тонкий трюк, который очень помогает в управлении зубчатыми массивами. Поскольку вы не знаете перед собой, сколько значений столбца может присутствовать - как только вы прочитали, вам нужен способ хранения количества присутствующих элементов столбца (для каждой строки в массиве). Простым и надежным методом является простое сохранение количества элементов столбца в строке в качестве значения первого столбца. Затем после сбора данных у вас есть информация как часть массива, которая предоставляет ключ для итерации по всем строкам и столбцам в массиве.
Включенные в рамках этого подхода, я создал функции специальности xstrtod
, xcalloc
, xrealloc_sp
(перераспределить массива одного указателя) и realloc_dp
(перераспределить для двойного указателя). Это не что иное, как стандартные функции с соответствующей проверкой ошибок, перемещенные в функцию, поэтому множество проверок проверки не облака основной части кода.
Быстрая реализация, которая считывает значения из stdin
может быть закодирован следующим образом:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <math.h> /* for HUGE_VALF, HUGE_VALL */
#define ROWS 32
#define COLS 32
#define MAXC 256
double xstrtod (char *str, char **ep);
void *xcalloc (size_t n, size_t s);
void *xrealloc_sp (void *p, size_t sz, size_t *n);
void *xrealloc_dp (void **p, size_t *n);
int main (void) {
char line[MAXC] = {0}; /* line buffer for fgets */
char *p, *ep; /* pointers for strtod */
double **array = NULL; /* array of values */
size_t row = 0, col = 0, nrows = 0; /* indexes, number of rows */
size_t rmax = ROWS, cmax = COLS; /* row/col allocation size */
/* allocate ROWS number of pointers to array of double */
array = xcalloc (ROWS, sizeof *array);
/* read each line in file */
while (fgets(line, MAXC, stdin))
{
p = ep = line; /* initize pointer/end pointer */
col = 1; /* start col at 1, store ncols in 0 */
cmax = COLS; /* reset cmax for each row */
/* allocate COLS number of double for each row */
array[row] = xcalloc (COLS, sizeof **array);
/* convert each string of digits to number */
while (errno == 0)
{
array[row][col++] = xstrtod (p, &ep);
if (col == cmax) /* if cmax reached, realloc array[row] */
array[row] = xrealloc_sp (array[row], sizeof *array[row], &cmax);
/* skip delimiters/move pointer to next digit */
while (*ep && *ep != '-' && (*ep < '0' || *ep > '9')) ep++;
if (*ep)
p = ep;
else /* break if end of string */
break;
}
array[row++][0] = col; /* store ncols in array[row][0] */
/* realloc rows if needed */
if (row == rmax) array = xrealloc_dp ((void **)array, &rmax);
}
nrows = row; /* set nrows to final number of rows */
printf ("\n the simulated 2D array elements are:\n\n");
for (row = 0; row < nrows; row++) {
for (col = 1; col < (size_t)array[row][0]; col++)
printf (" %8.2lf", array[row][col]);
putchar ('\n');
}
putchar ('\n');
/* free all allocated memory */
for (row = 0; row < nrows; row++)
free (array[row]);
free (array);
return 0;
}
/** string to double with error checking.
* #include <math.h> for HUGE_VALF, HUGE_VALL
*/
double xstrtod (char *str, char **ep)
{
errno = 0;
double val = strtod (str, ep);
/* Check for various possible errors */
if ((errno == ERANGE && (val == HUGE_VAL || val == HUGE_VALL)) ||
(errno != 0 && val == 0)) {
perror ("strtod");
exit (EXIT_FAILURE);
}
if (*ep == str) {
fprintf (stderr, "No digits were found\n");
exit (EXIT_FAILURE);
}
return val;
}
/** xcalloc allocates memory using calloc and validates the return.
* xcalloc allocates memory and reports an error if the value is
* null, returning a memory address only if the value is nonzero
* freeing the caller of validating within the body of code.
*/
void *xcalloc (size_t n, size_t s)
{
register void *memptr = calloc (n, s);
if (memptr == 0)
{
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
return memptr;
}
/** reallocate array of type size 'sz', to 2 * 'n'.
* accepts any pointer p, with current allocation 'n',
* with the type size 'sz' and reallocates memory to
* 2 * 'n', updating the value of 'n' and returning a
* pointer to the newly allocated block of memory on
* success, exits otherwise. all new memory is
* initialized to '0' with memset.
*/
void *xrealloc_sp (void *p, size_t sz, size_t *n)
{
void *tmp = realloc (p, 2 * *n * sz);
#ifdef DEBUG
printf ("\n reallocating '%zu' to '%zu', size '%zu'\n", *n, *n * 2, sz);
#endif
if (!tmp) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
p = tmp;
memset (p + *n * sz, 0, *n * sz); /* zero new memory */
*n *= 2;
return p;
}
/** reallocate memory for array of pointers to 2 * 'n'.
* accepts any pointer 'p', with current allocation of,
* 'n' pointers and reallocates to 2 * 'n' pointers
* intializing the new pointers to NULL and returning
* a pointer to the newly allocated block of memory on
* success, exits otherwise.
*/
void *xrealloc_dp (void **p, size_t *n)
{
void *tmp = realloc (p, 2 * *n * sizeof tmp);
#ifdef DEBUG
printf ("\n reallocating %zu to %zu\n", *n, *n * 2);
#endif
if (!tmp) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
p = tmp;
memset (p + *n, 0, *n * sizeof tmp); /* set new pointers NULL */
*n *= 2;
return p;
}
Compile
gcc -Wall -Wextra -Ofast -o bin/fgets_strtod_dyn fgets_strtod_dyn.c
Входной
$ cat dat/float_4col.txt
2078.62 5.69982 -0.17815 -0.04732
5234.95 8.40361 0.04028 0.10852
2143.66 5.35245 0.10747 -0.11584
7216.99 2.93732 -0.18327 -0.20545
1687.24 3.37211 0.14195 -0.14865
2065.23 34.0188 0.1828 0.21199
2664.57 2.91035 0.19513 0.35112
7815.15 9.48227 -0.11522 0.19523
5166.16 5.12382 -0.29997 -0.40592
6777.11 5.53529 -0.37287 -0.43299
4596.48 1.51918 -0.33986 0.09597
6720.56 15.4161 -0.00158 -0.0433
2652.65 5.51849 0.41896 -0.61039
Выход
$ ./bin/fgets_strtod_dyn <dat/float_4col.txt
the simulated 2D array elements are:
2078.62 5.70 -0.18 -0.05
5234.95 8.40 0.04 0.11
2143.66 5.35 0.11 -0.12
7216.99 2.94 -0.18 -0.21
1687.24 3.37 0.14 -0.15
2065.23 34.02 0.18 0.21
2664.57 2.91 0.20 0.35
7815.15 9.48 -0.12 0.20
5166.16 5.12 -0.30 -0.41
6777.11 5.54 -0.37 -0.43
4596.48 1.52 -0.34 0.10
6720.56 15.42 -0.00 -0.04
2652.65 5.52 0.42 -0.61
Проверка памяти
В любом коде вашей записи, которая динамически выделяет память, крайне важно, чтобы вы использовали ошибку памяти программу проверки, чтобы убедиться, вы не написали за/вне ваш выделенный блок памяти и подтвердить, что вы освободили всю память, которую вы выделили. Для Linux valgrind
это обычный выбор. Есть так много тонких способов злоупотребления блоком памяти, который может вызвать реальные проблемы, нет оправдания, чтобы не делать этого. Для каждой платформы есть аналогичные проверки памяти. Все они просты в использовании. Просто запустите свою программу.
$ valgrind ./bin/fgets_strtod_dyn <dat/float_4col.txt
==28022== Memcheck, a memory error detector
==28022== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==28022== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==28022== Command: ./bin/fgets_strtod_dyn
==28022==
the simulated 2D array elements are:
2078.62 5.70 -0.18 -0.05
5234.95 8.40 0.04 0.11
2143.66 5.35 0.11 -0.12
7216.99 2.94 -0.18 -0.21
1687.24 3.37 0.14 -0.15
2065.23 34.02 0.18 0.21
2664.57 2.91 0.20 0.35
7815.15 9.48 -0.12 0.20
5166.16 5.12 -0.30 -0.41
6777.11 5.54 -0.37 -0.43
4596.48 1.52 -0.34 0.10
6720.56 15.42 -0.00 -0.04
2652.65 5.52 0.42 -0.61
==28022==
==28022== HEAP SUMMARY:
==28022== in use at exit: 0 bytes in 0 blocks
==28022== total heap usage: 14 allocs, 14 frees, 3,584 bytes allocated
==28022==
==28022== All heap blocks were freed -- no leaks are possible
==28022==
==28022== For counts of detected and suppressed errors, rerun with: -v
==28022== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Там нет ничего сложного о чтении неизвестное количество rows
и неизвестное количество columns
из файла в C, но вы должны обратить особое внимание на то, как вы это делаете.Хотя вы можете ограничить массив квадратом (NxN
), нет причин, чтобы каждая строка не могла иметь различное количество столбцов (a jagged-array).
Ваш основной подход состоит в том, чтобы выделить память для массива или указатели для ввода double
для некоторого разумного ожидаемого количества строк. (#define ROWS 32
) Затем вы прочтете каждую строку. Для каждой прочитанной строки вы выделяете блок памяти для массива 'double' для некоторого разумно ожидаемого количества парных разрядов. (#define COLS 32
)
Затем вы преобразовываете каждую строку цифр, встречающихся в двойное значение, и храните их на array[row][col]
. (мы фактически начинаем хранить значения вcol = 1
и сохранять col = 0
, чтобы удерживать окончательное количество столбцов для этой строки). Вы отслеживаете номер, который вы добавили в массив, и если количество столбцов достигает выделенного вами номера, тогда вы realloc
массив для размещения дополнительных удвоений.
Вы продолжаете читать строки, пока не прочитаете все строки. Если вы достигнете своего первоначального предела по количеству строк, вы просто realloc
массив, как вы делали с cols
.
Теперь у вас есть все ваши данные и можете делать с ними то, что вы будете. Когда все будет готово, не забудьте указать free
всю память, которую вы выделили. Дайте мне знать, если у вас есть вопросы.
Быстрый Brown Fox Расстались Файл
Там не является еще один бит дополнительной надежности, что вы можете создать в код, который в основном позволит вам прочитать любую строку данных, независимо от того, сколько нежелательной может быть включен в файл. Не имеет значения, если строка-значения разделенные запятой, точку с запятой, пространство, отделенное, или отделены друг от быстрый коричневая лиса. С небольшой помощью синтаксического анализа вы можете предотвратить ошибки чтения, вручную продвигаясь к началу следующего номера. Быстрое добавление в контексте будет:
while (errno == 0)
{
/* skip any non-digit characters */
while (*p && ((*p != '-' && (*p < '0' || *p > '9')) ||
(*p == '-' && (*(p+1) < '0' || *(p+1) > '9')))) p++;
if (!*p) break;
array[row][col++] = xstrtod (p, &ep);
...
Пропуская нецифровые позволит читать практически любой здравомыслящий файл с любым типом разделителем без вопроса. Возьмем, к примеру, одни и те же номера, используемые первоначально, но теперь в следующем формате в файле данных:
$ cat dat/float_4colmess.txt
The, 2078.62 quick 5.69982 brown -0.17815 fox; -0.04732 jumps
5234.95 over 8.40361 the 0.04028 lazy 0.10852 dog
and the 2143.66 dish ran 5.35245 away 0.10747 with -0.11584
the spoon, 7216.99 2.93732 -0.18327 -0.20545
1687.24 3.37211 0.14195 -0.14865
2065.23 34.0188 0.1828 0.21199
2664.57 2.91035 0.19513 0.35112
7815.15 9.48227 -0.11522 0.19523
5166.16 5.12382 -0.29997 -0.40592
6777.11 5.53529 -0.37287 -0.43299
4596.48 1.51918 -0.33986 0.09597
6720.56 15.4161 -0.00158 -0.0433
2652.65 5.51849 0.41896 -0.61039
Даже с этой безумной формат, код не имеет никаких проблем, правильно читать все числовые значения в массив правильно.
Является ли ваш главный подобным, или он возвращает 'int'? Кроме того, не бросайте 'void *', в частности вам не нужно '(double *) malloc()', если вам нужен актерский состав, вы используете неправильный язык или неправильный компилятор. И 'malloc()' ing и 'free()' массива снова и снова очень плохо, используйте предопределенное значение, а также используйте 'realloc()' вместо того, чтобы освобождать вручную. Вы попробовали 'fgets()'? –
вы освобождаете блок памяти, который не был выделен 'malloc'. – immibis
** [Попробуйте это] (https://stackoverflow.com/a/28335093/3386109). ** – user3386109