Как сохранить данные в CSV-файле в структуре, а затем вернуть эту структуру - PullRequest
1 голос
/ 04 июля 2019

Это мой первый пост о переполнении стека, и надеюсь, что кто-то сможет указать мне правильное направление. Я пишу функцию C, где моей целью является чтение файла CSV. Затем данные в файле передаются в структурный массив, который я затем хотел бы вернуться к вызову функции в main (), получая доступ к данным для дальнейшего использования. Как правильно прочитать, а затем вернуть полный массив структуры?

Эта функция является дополнением к существующей программе ПЛК, где в настоящий момент все системные параметры хранятся в сохраняемой памяти. Целью является чтение / запись параметров в файл CSV для резервного копирования. Я подозреваю, что я делаю что-то не так в цикле while, но на данный момент не могу понять, что именно. Также может быть, я не правильно использую указатели. Файл CSV выглядит так:

2;motor nominal current;1700
3;motor nominal speed;2500.0
4;motor nominal power;1200.0
5;motor nominal voltage;690.0
6;Enable motor heating;TRUE
7;Motor heating time on;40.0

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

Вот программа, содержащая функцию:

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

#define BSIZE 80

struct parameter{
    int id;
    char *name;
    char *value;
};

struct parameter* readCSV(const char *file)
{
    char buffer[BSIZE];
    FILE *f;
    char *field;

    // open the CSV file
    f = fopen(file,"r");
    if( f == NULL)
    {
        printf("Unable to open file '%s'\n",file);
        exit(1);
    }
    static struct parameter *parameters[BSIZE];

        int i = 0;
    // read the data
    while(fgets(buffer,BSIZE,f) != NULL)
    {
        parameters[i] =(struct parameter*)malloc(sizeof(struct parameter));

         // get id
        field = strtok(buffer,";");
        parameters[i]->id = atoi(field);

        // get name
        field = strtok(NULL,";");
        parameters[i]->name = field;

        // get value
        field = strtok(NULL,";");
        parameters[i]->value = field;

        // display the result
        printf("ID%d:\t%s\t%s\n",parameters[i].id, parameters[i].name, parameters[i].value);

        i++;
    }

    //close file
    fclose(f);

    return *parameters;

}

int main()
{
    struct parameter *parameters;

    parameters = readCSV("QD_Config.csv");

        printf("ID%d:\t%s\t%s\n",parameters[0]->id, parameters[0]->name, parameters[0]->value);

    return(0);
}

Я могу напечатать содержимое файла, но не могу правильно сохранить массив структуры перед его передачей. В main () при вызове функции я получаю только фамилию и значение в файле, но с идентификатором первого.

Ответы [ 2 ]

0 голосов
/ 05 июля 2019
    #include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BSIZE 80

struct parameter{
    int id;
    char *name;
    char *value;
    struct parameter *next;//If you dont sure how many lins in csv you need this
};
typedef struct parameter parameter;
//I'm lazy too type struct
parameter* CreateNewQ_Q(){
    parameter *Q_Q=(parameter*)malloc(sizeof(parameter));
    Q_Q->name=NULL;//Nothing at first
    Q_Q->value=NULL;//Nothing at first
    Q_Q->next=NULL;//Nothing at first
    return Q_Q;
}
void readCSV(const char *file,parameter *Q_Q)
{
    char buffer[BSIZE];
    FILE *f;
    char *field;
    parameter* A_A=Q_Q;
    // open the CSV file
    f = fopen(file,"r");
    if( f == NULL)
    {
        printf("Unable to open file '%s'\n",file);
        exit(1);
    }


    // read the data
    while(fgets(buffer,BSIZE,f) != NULL)
    {
        if(A_A->next==NULL){//Next Nothing So Create after it
            A_A->next=CreateNewQ_Q();
        }
        A_A=A_A->next;//A_A is New A_A now
         // get id
        field = strtok(buffer,";");
        A_A->id = atoi(field);

        // get name
        field = strtok(NULL,";");
        //Q_Q
            //<--------Here alloc memory for your name because strtok not alloc new memory it just return a pointer in buffer[?]-------------->
            A_A->name=(char *)malloc((sizeof(strlen(field)+1)*sizeof(char)));//+1 Becuz '\0' at end of string is necessary
            //<--------Here Copy Result-------------->
            strcpy(A_A->name, field);
        //Q_Q

        // get value
        field = strtok(NULL,";");
        //Q_Q
            //<--------Here alloc memory for your value because strtok not alloc new memory it just return a pointer in buffer[?]-------------->
            A_A->value=(char *)malloc((sizeof(strlen(field)+1)*sizeof(char)));//+1 Becuz '\0' at end of string is necessary
            //<--------Here Copy Result-------------->
            strcpy(A_A->value, field);
        //Q_Q

        // display the result
        printf("ID%d:\t%s\t%s\n",A_A->id, A_A->name, A_A->value);

    }

    //close file
    fclose(f);



}
void DeleteAllQ_Q(parameter *Q_Q){
    if(Q_Q->next){
        DeleteAllQ_Q(Q_Q->next);
        Q_Q->next=NULL;
    }else{
        free(Q_Q->name);//I dont have next so i'm free
        free(Q_Q->value);
        free(Q_Q);
    }

}
int main()
{
    //memory control is important!!!!!!!!!!!!!!
    parameter *parameters=CreateNewQ_Q();

    readCSV("QD_Config.csv",parameters);
    printf("Ok Load Done A_A\n");
    for(parameter *loopQ_Q=parameters->next;loopQ_Q!=NULL;loopQ_Q=loopQ_Q->next){
        printf("ID%d:\t%s\t%s\n",loopQ_Q->id, loopQ_Q->name, loopQ_Q->value);
    }


    DeleteAllQ_Q(parameters);//free parameters's next and next's next and....
    free(parameters);//free self

    return(0);
}

Я провожу некоторое время, я думаю, что этот метод управления памятью более безопасен!

0 голосов
/ 04 июля 2019

Ваша проблема (вероятно) заключается в том, что функция strtok возвращает указатель на строку, которую вы токенизируете.Он не создает для вас новую строку.

Это означает, например, что

field = strtok(NULL,";");
parameters[i]->name = field;

сделает parameters[i]->name указателем на некоторый символ в buffer.И как только функция readCSV возвращает переменную buffer, она завершает свое время жизни и перестает существовать, оставляя вас с неверным указателем.

Вам необходимо самостоятельно выделить память для строк и скопировать в них данные,Это делается либо путем создания массивов элементов структуры и использования strcpy для копирования строки в эти массивы, либо с использованием нестандартной, но общедоступной функции strdup (которая динамически распределяет память кучи и копирует строку вэто).


Есть еще одна проблема, связанная с возвратом ваших структур:

return *parameters;

равно

return parameters[0];

То есть вы возвращаете указатель наодна parameter структура.

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

return parameters;  // Return the whole array

Но обратите внимание, что он будет затухать до указателя на свой первый элемент (то есть &parameters[0]), имеющего тип struct parameter **, поэтому вам необходимо соответствующим образом настроить тип возвращаемого значения.

Вам также нужно инициализировать parameters для нулевых указателей, иначе будет трудно найтиконец массива:

static struct parameter *parameters[BSIZE] = { NULL };

Однако лучшее решение, которое я бы рекомендовал, состоит в том, чтобы передать массив в качестве аргумента и вернуть количество заполненных элементов. Затем вы можете использоватьмассив структурных объектов (вместо массива структурных указателей) и не требует динамического выделения ресурсов и риска утечки памяти.

...