Лучший способ загрузить файл .csv аналогично MATLAB на языке Си - PullRequest
0 голосов
/ 16 июня 2019

Я пытаюсь преобразовать имитацию MATLAB в C без использования встроенного кодера, чтобы попытаться научить себя C. В MATLAB для импорта и использования данных из файла Excel / csv вы либо вручную импортируете данные с помощью графического интерфейса пользователя в рабочее пространство, где вы можете затем сохранить его как файл .mat и использовать оттуда переменные, или не рекомендуется использовать метод csvread. Я пытаюсь выполнить аналогичную операцию в C.

Я могу получить данные из файла CSV (720 строк на 3 столбца), используя приведенный ниже код, однако я изо всех сил пытаюсь передать данные из функции load_PV в функцию main(), поскольку выводимые данные являются многомерными массив. Мой главный вопрос: если это лучший метод и использовать его, или если бы я мог / должен создать функцию, которая загружает каждый столбец индивидуально как новую переменную в цикле и передает их в функцию main ()

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

float CA[720];
float P[720];
float V[720];
//~ static float ARRAY[720];

float load_PV(int r, int c, float DATA[720][3])
{
  char buf[720];
  //~ static float ARRAY[720];

  FILE *fp = fopen("PV_Data.csv", "r");

  if(!fp)
  {
    printf("Could Not Open File\n");
  }
  int i = 1;

  while(fgets(buf, 720, fp))
  {
    CA[i] = atof(strtok(buf, ","));
    P[i] = atof((strtok(NULL, ",")));
    V[i] = atof((strtok(NULL, ",")));
    DATA[i][1] = CA[i];
    DATA[i][2] = P[i];
    DATA[i][3] = V[i];
    i++;
  }

  //~ printf("%f\n", ARRAY[540][3]);

  return(DATA[720][3]);
}

int main()
{
    int r = 720;
    int c = 3;
    float DATA[r][c];

    float data = load_PV(r, c, DATA[720][3]);

    printf("%f\n", data);

    //~ int i = 1;

    //~ for(i = 1; i<=720; i++);
    //~ {
        //~ printf("%f", data[i][1]);
        //~ printf("\t");
        //~ printf("%f", data[i][2]);
        //~ printf("\t");
        //~ printf("%f\n", data[i][3]);
    //~ }
}

Я ожидал, так как я не настолько знаком с C (вы можете сказать?: P), что на выходе будет просто массив с плавающей запятой 720x3, однако при компиляции я получаю следующие сообщения об ошибках:

gcc -Wall -o "Test2" "Test2.c" (in directory: C:\Users\Student\Dropbox\C Projects\MATLAB_to_C)
Test2.c: In function 'main':
Test2.c:47:29: error: incompatible type for argument 3 of 'load_PV'
  float data = load_PV(r, c, DATA[720][3]);
                             ^
Test2.c:11:8: note: expected 'float (*)[3]' but argument is of type 'float'
  float load_PV(int r, int c, float DATA[720][3])
        ^
Compilation failed.

Я не настолько знаком с указателями, как с одним из аспектов C, который я пытаюсь выучить, я видел, что вы можете сделать это с ними, но я не совсем понял примеры, поэтому Я не использовал их, поскольку я пытаюсь научиться не копировать. Так допустим ли этот подход или другой более широко используется?

P.S. Это не домашнее задание. Я только что закончил университет и увидел, что больше компаний ищут C, чем MATLAB, поэтому стараются учиться для будущего развития. Это упражнение для хобби / профессионального развития

Ответы [ 2 ]

1 голос
/ 16 июня 2019

На первой итерации вы могли бы написать что-то вроде:

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

typedef struct {
    float CA;
    float P;
    float V;
} Entry;


#define ROWS 720

Entry *load_PV() {
    char buf[1024];

    FILE *fp;
    if ((fp = fopen("PV_Data.csv", "r")) == NULL) {
        printf("Could Not Open File\n");
        exit(-1);
    }

    Entry *entries = malloc(sizeof(Entry) * ROWS);

    for (int i = 0; i < ROWS && fgets(buf, sizeof(buf), fp); i++) {
        entries[i].CA = atof(strtok(buf, ","));
        entries[i].P = atof((strtok(NULL, ",")));
        entries[i].V = atof((strtok(NULL, ",")));
    }

    fclose(fp);

    return entries;
}

int main() {
    Entry *entries = load_PV();
    for (int i = 0; i < ROWS; i++) {
        Entry entry = entries[i];
        printf("%f %f %f\n", entry.CA, entry.P, entry.V);
    }
    free(entries);
    return 0;
}

Изменения

  • , поскольку каждая строка состоит из значений CA, P и V, мы могли бы использовать структуру
  • определение определяет количество строк
  • нам динамически выделяется память для количества строк
  • ФАЙЛ * закрыт fclose (fp)

Более динамичное и надежное решение

Приведенный выше код является первой итерацией, но, вероятно, он не такой динамичный и надежный, как вы могли бы пожелать даже для проекта хобби.

Итак, что следует / можно улучшить:

  • размеры (количество строк и столбцов) должны быть динамическими
  • он должен обрабатывать пропущенные строки в файле
  • он должен обрабатывать пропущенные столбцы в файле
  • количество прочитанных строк должно быть возвращено

Тогда код может выглядеть следующим образом во второй итерации:

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


int load_PV(float **dataPtr, int r, int c) {
    char buf[1024];

    FILE *fp;
    if ((fp = fopen("PV_Data.csv", "r")) == NULL) {
        fprintf(stderr, "Could Not Open File\n");
        exit(-1);
    }

    float *data = calloc(r * c, sizeof(float));
    if (!data) {
        *dataPtr = NULL;
        return 0;
    }
    *dataPtr = data;

    int row = 0;
    for (row = 0; row < r && fgets(buf, sizeof(buf), fp); row++) {
        char *token = strtok(buf, ",");
        if(token) {
            data[row * c] = strtof(token, NULL);
            for (int col = 1; col < c; col++) {
                token = strtok(NULL, ",");
                if(token) {
                    data[row * c + col] = strtof(token, NULL);
                } else {
                    fprintf(stderr, "missing value in row %d\n", row);
                }
            }
        } else {
            fprintf(stderr, "missing value in row %d\n", row);
        }
    }

    fclose(fp);

    return row;
}

int main() {
    int r = 720;
    int c = 3;
    float *data;

    int number_of_Rows = load_PV(&data, r, c);
    for (int i = 0; i < number_of_Rows; i++) {
        float *rowData = &data[i * c];
        printf("[%d]: ", i);
        for(int col = 0; col < c; col++) {
            printf("%f ", rowData[col]);
        }
        printf("\n");
    }
    if(data) {
        free(data);
    }
    return 0;
}

Так как это работает?

В main есть указатель с именем data для плавания. адрес этого указателя передается load_PV вместе с размерами (строками / столбцами). Там память для поплавков динамически распределяется и заполняется нулями. Указанное количество поплавков считывается для каждой строки. Если данные отсутствуют, сообщение записывается в stderr. Количество строк возвращается. Затем данные просто выводятся на консоль в главном, принимая во внимание размеры. Наконец, динамически назначенные данные освобождаются.

Дальнейшие итерации

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

0 голосов
/ 16 июня 2019

Вы смешиваете синтаксис для определения / объявления массива и доступа к массиву.

Это объявление параметра:

float DATA[720][3]

Здесь DATA объявлено какмассив.

Это доступ к члену массива:

return(DATA[720][3]);

Вы получаете доступ к элементу в позиции [720][3] этого массива.Это единственный элемент типа float.Любое, что вам нужно знать, это то, что индексирование в C начинается с 0. Это означает, что индекс [720][3] является выходом за границы.Максимальный диапазон: [719][2].

Та же проблема возникает при вызове этой функции:

float data = load_PV(r, c, DATA[720][3]);

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

Если вы хотите передать массив, вам нужно использовать

float data = load_PV(r, c, DATA);

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

int func(int array[123])

, тип array не int[123], а только int* или int(*).Также sizeof(array) - это размер указателя, а не размер всего массива.

То же самое для вашей функции:

float load_PV(int r, int c, float DATA[720][3])

Здесь тип третьего параметра не float[720][3] но float(*)[3].Поэтому компилятор жалуется на несоответствие между ожидаемым и переданным типом параметра:

test2.c:11:8: note: expected 'float (*)[3]' but argument is of type 'float'
  float load_PV(int r, int c, float DATA[720][3])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...