【问题标题】:How to store data in a CSV file to a structure and then return that structure如何将 CSV 文件中的数据存储到结构中,然后返回该结构
【发布时间】:2019-11-15 03:39:59
【问题描述】:

这是我关于堆栈溢出的第一篇文章,希望有人能够指出我正确的方向。我正在编写一个 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号。

【问题讨论】:

    标签: c arrays csv malloc structure


    【解决方案1】:

    您(可能)遇到的问题是strtok 函数返回一个指向您要标记的字符串的指针。它不会为您创建新字符串。

    这意味着例如

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

    将使parameters[i]-&gt;name 指向buffer 中的某个字符。一旦函数readCSV 返回变量buffer 结束它的生命周期并且不再存在,留下无效的指针。

    您需要自己为字符串分配内存并将数据复制到它们。这可以通过使结构成员数组并使用strcpy 将字符串复制到这些数组来完成,或者通过使用非标准但常用的strdup 函数(它动态分配堆的内存并将字符串复制到它)。


    还有一个与返回结构有关的问题:

    return *parameters;
    

    等于

    return parameters[0];
    

    也就是说,您返回一个指向单个 parameter 结构的指针。

    如果你想返回整个数组,你应该这样做

    return parameters;  // Return the whole array
    

    但请注意,它会衰减为指向其第一个元素(即&amp;parameters[0])的指针,其类型为struct parameter **,因此您需要适当调整返回类型。

    您还需要将parameters 初始化为空指针,否则将很难找到数组的末尾:

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

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

    【讨论】:

    • 谢谢,我实现了 strcpy()。例如strcpy(参数[i]).id,字段);并将结构成员更改为字符串数组。例如字符 ID[45]。但是返回值仍然是空的。我显然需要阅读更多关于该主题的内容。我可能仍然没有正确分配。
    • @T.Strand 已更新 return 问题。
    【解决方案2】:
        #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);
    }
    

    我花点时间,我觉得这种控制内存的方法更安全!

    【讨论】:

    • 感谢您的贡献。我同意链接列表通常是一个很好的解决方案,因为参数列表的大小可能会随着时间的推移而增长。但是我还没有找到一个很好的解决方案来解决如何在 PLC 应用程序中实现对它的支持。我也不担心冲突,因为每个参数的 ID 都是唯一的,并且会设置结构数组的索引。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多