чтение данных из большого файла в структуру в C - PullRequest
0 голосов
/ 08 марта 2019

Я новичок в программировании на Си. Мне нужно эффективно читать миллионы из файла, используя структуру в файле. Ниже приведен пример входного файла.

2,33.1609992980957,26.59000015258789,8.003999710083008
5,15.85200023651123,13.036999702453613,31.801000595092773
8,10.907999992370605,32.000999450683594,1.8459999561309814
11,28.3700008392334,31.650999069213867,13.107999801635742

У меня есть текущий код, показанный ниже, он выдает ошибку «Ошибка в файле» Предполагается, что файл пуст, но файл содержит данные

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

struct O_DATA
{
    int index;
    float x;
    float y;
    float z;
};

int main ()
{
    FILE *infile ;
    struct O_DATA input;
    infile = fopen("input.dat", "r");
    if (infile == NULL);
    {
            fprintf(stderr,"\nError file\n");
            exit(1);
    }
    while(fread(&input, sizeof(struct O_DATA), 1, infile))
            printf("Index = %d X= %f Y=%f Z=%f", input.index , input.x ,   input.y , input.z);
    fclose(infile);
    return 0;
}

Мне нужно эффективно читать и хранить данные из входного файла для дальнейшей обработки. Любая помощь могла бы быть полезна. Спасибо в Advnace. ~
~
~

Ответы [ 5 ]

1 голос
/ 08 марта 2019

У вас уже есть твердые ответы относительно синтаксиса / структур / и т. Д., Но я предложу другой метод для чтения данных в самом файле: мне нравится решение CSVIterator Мартина Йорка.Это мой подход к обработке CSV, потому что он требует меньше кода для реализации и имеет дополнительное преимущество, заключающееся в том, что его легко модифицировать (то есть вы можете редактировать определения CSVRow и CSVIterator в зависимости от ваших потребностей).

Вотнаиболее полный пример использования неотредактированного кода Мартина без структур или классов. По моему мнению, и особенно для новичка, легче начать разработку кода с более простых приемов.Когда ваш код начинает обретать форму, становится намного понятнее, почему и где вам нужно реализовывать более абстрактные / продвинутые устройства.

Обратите внимание, что это технически необходимо будет скомпилировать с C ++ 11 или более поздней версией из-за моего использования std :: stod (и, возможно, некоторых других вещей, которые я тоже забыл), поэтому возьмитеэто с учетом:

//your includes
//...
#include"wherever_CSVIterator_is.h"

int main (int argc, char* argv[]) 
{
  int index;
  double tmp[3]; //since we know the shape of your input data
  std::vector<double*> saved = std::vector<double*>();
  std::vector<int> indices;

  std::ifstream file(argv[1]);
  for (CSVIterator loop(file); loop != CSVIterator(); ++loop) { //loop over rows
    index = (*loop)[0]; 
    indices.push_back(index); //store int index first, always col 0
    for (int k=1; k < (*loop).size(); k++) {                    //loop across columns
       tmp[k-1] = std::stod((*loop)[k]); //save double values now
    }
    saved.push_back(tmp);
  }

 /*now we have two vectors of the same 'size'
  (let's pretend I wrote a check here to confirm this is true), 
  so we loop through them together and access with something like:*/

  for (int j=0; j < (int)indices.size(); j++) {
    double* saved_ptr = saved.at(j); //get pointer to first elem of each triplet
    printf("\nindex: %g |", indices.at(j));
    for (int k=0; k < 3; k++) {
      printf(" %4.3f ", saved_ptr[k]);
    }
    printf("\n");
  }
}

Меньше суеты, чтобы писать, но более опасно (если сохранено [] выходит из области видимости, у нас проблемы).Также присутствует некоторое ненужное копирование, но мы выигрываем от использования контейнеров std :: vector вместо того, чтобы точно знать, сколько памяти нам нужно выделить.

1 голос
/ 08 марта 2019

У вас неверный ; после теста if (infile == NULL) - попробуйте удалить это ...

[Редактировать: 2-й на 9 секунд!: -)]

1 голос
/ 08 марта 2019

Сначала выясните, как преобразовать одну строку текста в данные

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

struct my_data
{
  unsigned int index;
  float x;
  float y;
  float z;
};



struct my_data *
deserialize_data(struct my_data *data, const char *input, const char *separators)
{
  char *p;                      
  struct my_data tmp;

  if(sscanf(input, "%d,%f,%f,%f", &data->index, &data->x, &data->y, &data->z) != 7)
    return NULL;
  return data;
}

deserialize_data(struct my_data *data, const char *input, const char *separators)
{
  char *p;                      
  struct my_data tmp;
  char *str = strdup(input);    /* make a copy of the input line because we modify it */

  if (!str) {               /* I couldn't make a copy so I'll die */
      return NULL;
  }

  p = strtok (str, separators); /* use line for first call to strtok */
  if (!p) goto err;
  tmp.index = strtoul (p, NULL, 0);         /* convert text to integer */

  p = strtok (NULL, separators);    /* strtok remembers line */
  if (!p) goto err;
  tmp.x = atof(p);

  p = strtok (NULL, separators);
  if (!p) goto err;
  tmp.y = atof(p);

  p = strtok (NULL, separators);
  if (!p) goto err;
  tmp.z = atof(p);

  memcpy(data, &tmp, sizeof(tmp)); /* copy values out */
  goto out;

err:
  data = NULL;

out:
  free (str);
  return data;
}

int main() {
    struct my_data somedata;
    deserialize_data(&somedata, "1,2.5,3.12,7.955", ",");
    printf("index: %d, x: %2f, y: %2f, z: %2f\n", somedata.index, somedata.x, somedata.y, somedata.z);
}

Объедините этос чтением строк из файла:

только основная функция здесь (вставьте остаток из предыдущего примера)

   int
   main(int argc, char *argv[])
   {
       FILE *stream;
       char *line = NULL;
       size_t len = 0;
       ssize_t nread;
       struct my_data somedata;

       if (argc != 2) {
           fprintf(stderr, "Usage: %s <file>\n", argv[0]);
           exit(EXIT_FAILURE);
       }

       stream = fopen(argv[1], "r");
       if (stream == NULL) {
           perror("fopen");
           exit(EXIT_FAILURE);
       }

       while ((nread = getline(&line, &len, stream)) != -1) {
           deserialize_data(&somedata, line, ",");
           printf("index: %d, x: %2f, y: %2f, z: %2f\n", somedata.index, somedata.x, somedata.y, somedata.z);
       }

       free(line);
       fclose(stream);
       exit(EXIT_SUCCESS);
   }
1 голос
/ 08 марта 2019
if (infile == NULL);
{ /* floating block */ }

Вышеуказанное if является полным утверждением, которое ничего не делает независимо от значения infile. «Плавающий» блок выполняется независимо от того, что содержит infile.
Снимите точку с запятой, чтобы «прикрепить» «плавающий» блок к if

if (infile == NULL)
{ /* if block */ }
0 голосов
/ 08 марта 2019

Не указывайте пример входного файла. Укажите свой ввод формат файла - по крайней мере, на бумаге или в комментариях - например, в нотации EBNF (поскольку ваш пример текстовый ... это не a двоичный файл ). Решите, должны ли цифры быть в разных строках (или вы можете принять файл с одной огромной строкой, состоящей из миллиона байтов; прочитайте о формате с разделителями-запятыми ). Затем введите код parser для этого формата. В вашем случае вполне вероятно, что достаточно простого анализа рекурсивного спуска (и ваш конкретный парсер даже не будет использовать recursion ).

Узнайте больше о <stdio.h> и его подпрограммах . Потратьте время, чтобы внимательно прочитать эту документацию. Поскольку ваш ввод текстовый , а не двоичный , вам не нужно fread . Обратите внимание, что входные подпрограммы могут быть неудачными, и вы должны обработать случай сбоя.

Конечно, fopen может потерпеть неудачу (например, потому что ваш рабочий каталог - это не то, что вы считаете). Вам лучше использовать perror или errno , чтобы узнать больше о причине сбоя. Так по крайней мере код:

infile = fopen("input.dat", "r");
if (infile == NULL) {
  perror("fopen input.dat");
  exit(EXIT_FAILURE);
}

Обратите внимание, что точки с запятой (или их отсутствие) очень важны в C (без точки с запятой после условия if). Прочитайте еще раз основной синтаксис C языка . Читайте о Как отлаживать небольшие программы . Включите все предупреждения и отладочную информацию при компиляции (с GCC , компилируйте с gcc -Wall -g как минимум). Предупреждения компилятора очень полезны!

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

Вы, вероятно, будете читать каждую строку , используя fgets (или getline ), и анализировать каждую строку в отдельности. Вы можете выполнить этот анализ с помощью sscanf (возможно, %n может быть полезным) - и вы хотите использовать счетчик возврата sscanf. Вы также можете использовать strtok и / или strtod для такого анализа.

Убедитесь, что ваш синтаксический анализ и вся ваша программа правильны . На современных компьютерах (они очень быстрые, и большую часть времени ваш входной файл находится в кеше ), весьма вероятно, что он будет достаточно быстрым. Миллион строк может быть прочитан довольно быстро (если в Linux вы можете сравнить время анализа с временем, используемым wc для подсчета строк в вашем файле). На моем компьютере (мощный рабочий стол Linux с процессором AMD2970WX - у него много ядер, но ваша программа использует только один, 64 ГБ ОЗУ и SSD-диск), можно прочитать миллион строк (wc) менее чем за 30 миллисекунды, поэтому я предполагаю, что вся ваша программа должна выполняться менее чем за полсекунды, если дано миллион строк ввода и если дальнейшая обработка проста (в линейном времени).

Вы, вероятно, заполните большой массив struct O_DATA, и этот массив, вероятно, должен быть динамически выделен и перераспределен при необходимости. Подробнее о C динамическом выделении памяти . Внимательно прочитайте о C процедурах управления памятью . Они могут потерпеть неудачу, и вы должны справиться с этой ошибкой (даже если это очень маловероятно). Вы, конечно, не хотите перераспределять этот массив в каждом цикле. Вы, вероятно, могли бы выделить его в некоторой геометрической прогрессии (например, если размер этого массива size, вы будете вызывать realloc или новый malloc для некоторых int newsize = 4*size/3 + 10; только тогда, когда старый size слишком мало). Конечно, ваш массив, как правило, будет немного больше того, что действительно нужно, но память довольно дешевая, и вам разрешено «потерять» ее.

Но StackOverflow - это , а не сайт "сделай мою домашнюю работу". Я дал несколько советов выше, но вы должны сделать свою домашнюю работу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...