0

Я собираюсь реализовать динамическую матричную структуру (которая хранит двойные значения), и у меня возникли проблемы с чтением из файла.Преобразование строки в парный разряд в C

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

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

Чтобы исправить это, я сначала fscanf() строка по характеру с функцией. Он сохраняет значения в строке, которая представляет собой ровно одну строку.

Затем я использую sscanf() для сканирования строки для двойных значений и сохранения их в двойном массиве. После преобразования я освобожу строку. Это делается в функции chararray_to_doublearray.

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

/* Converts a character array to a double array and returns a pointer to it. Frees the space of the character array, as it's no longer needed. */ 
double *chararray_to_doublearray(char **chararray) 
{ 
    int i; 
    int elements = 0; 
    double *numbers=NULL; 
    double newnumber; 
    while (sscanf(*chararray, "%lf ", &newnumber) == 1) { 
     double* newarray = (double*) malloc(sizeof(double) * (elements+1)); 
     for (i = 0; i < elements; ++i) 
      newarray[i] = numbers[i]; 
     free(numbers); 
     numbers = newarray; 
     numbers[elements] = newnumber; 
     ++elements; 
    } 
    free(*chararray); 
    return numbers; 
} 

И основной() функция вызова только chararray_to_doublearray функция:

main() 
{ 
    int i; 
    double *numbers; 
    char string[50]="12.3 1.2 3.4 4 0.3"; 
    numbers=chararray_to_doublearray(&string); 
    free(numbers) 
    return 0; 
} 

Итак, подведем итог: я не мог найти хорошую реализацию чтения двойных значений из пользователя (или из файла) до конца строки. Это моя реализация. Есть ли у вас какие-то идеи, что может быть не так с этим?

Привет,

naroslife

+0

Является ли ваш главный подобным, или он возвращает 'int'? Кроме того, не бросайте 'void *', в частности вам не нужно '(double *) malloc()', если вам нужен актерский состав, вы используете неправильный язык или неправильный компилятор. И 'malloc()' ing и 'free()' массива снова и снова очень плохо, используйте предопределенное значение, а также используйте 'realloc()' вместо того, чтобы освобождать вручную. Вы попробовали 'fgets()'? –

+0

вы освобождаете блок памяти, который не был выделен 'malloc'. – immibis

+0

** [Попробуйте это] (https://stackoverflow.com/a/28335093/3386109). ** – user3386109

ответ

0

Это XY problem. У вас действительно нужно «fscanf() линией-по-характеру»? Из-за этого вы задали слишком много вопросов в неправильном направлении?

Рассмотрите это: %lf обозначает преобразование символов в double, которые вы выбрали ... Он останавливается сразу же, когда нет более подходящих символов для преобразования ... и новая строка не подходит для преобразования ... Есть еще лампочка, светящаяся в голове?

В вашем случае пробел, следующий за %lf, в строке формата приводит к отбрасыванию полезной информации (независимо от того, является ли пробел символом новой строки или нет). СТОП! Вы зашли слишком далеко, и следствие состоит в том, что теперь вам нужна промежуточная функция преобразования массива символов, которая не нужна для раздувания.

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

например.

double f; 
int x = scanf("%lf", &f); 
int c; 
do { 
    c = getchar(); 
} while (isspace(c) && c != '\n'); 
if (c != '\n') { 
    ungetc(c, stdin); 
} 

См. Выше, как я смог отличить новую строку от невооруженного белого пространства?

+0

Это действительно проблема XY, моя плохо. Поскольку эта реализация наиболее близка к моей оригинальной идее и на самом деле гораздо более удобна в использовании, чем работа с массивом символов, в настоящее время я работаю над внедрением этого метода. Благодаря! – naroslife

0

Нет ничего сложного в том, что вы читаете неизвестное количество двойных значений из файла или 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 

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

+0

Благодарим вас за подробный ответ. Это действительно надежная реализация, но я чувствую, что она намного выше моего уровня мастерства, и мне было бы не удобно ее использовать, поскольку это домашнее задание, над которым я работаю. Но я сохранил его для дальнейшего использования! – naroslife

+0

Рад, что я мог хотя бы немного помочь. Это будет незадолго до того, как вы будете регулярно использовать этот тип реализации. Есть немного оснований и основ, чтобы ознакомиться с тем, как C-lightbulb подмигивает, но как только это произойдет, нет другого языка, который предлагает низкоуровневый контроль над вычислениями, чем C (ассемблер исключен). Удачи. –

Смежные вопросы