如何将CSV文件中的数据存储到结构,然后返回该结构

时间:2019-07-04 11:43:02

标签: c arrays csv malloc structure

这是我在堆栈溢出时的第一篇文章,希望有人能指出我正确的方向。我正在写一个C函数,我的目标是读取一个csv文件。然后,文件中的数据将传递到结构数组,然后我想返回到main()中的函数调用,以访问数据以备将来使用。如何正确读取然后返回完整的结构数组?

此功能是现有PLC程序的附加功能,此刻所有系统参数都存储在保留存储器中。目的是将参数读/写到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()中,调用函数时,我只获得文件中的姓氏和值,但具有名字的ID号。

2 个答案:

答案 0 :(得分:0)

您(可能)遇到的问题是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 };

但是,我更推荐一个更好的解决方案,是将数组作为参数传递,并返回要填充的元素数。然后可以使用结构对象数组(而不是结构指针数组) ),而不必进行任何动态分配,也可以避免内存泄漏的风险。

答案 1 :(得分:0)

    #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);
}

我花了一段时间,我认为这种方法控制内存更安全!