解析CSV并将数据存储在结构数组中?

6xfqseft  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(111)

我想尝试做的是解析一个CSV,其中包含一个人的名字、姓氏和体重,以及一个结构数组中的这些数据。这是我到目前为止的代码。我能够解析并打印出CSV的值,但我不太确定如何将这些值存储到结构数组中。我希望能够从person. c访问CSV的值,但实际上是从vector. c中读取的,这意味着我必须从vector. c中的person. c中调用函数readCSV(),你们谁能帮我一下吗?

//vector.h
#ifndef VECTOR_H_
#define VECTOR_H_
#include "person.h"

typedef struct
{
    Person *personArray;
    int sizeArray;
    int count;
}Vector;

//person.h
#ifndef PERSON_H_
#define PERSON_H_

typedef struct
{
    const char *firstName, *lastName;
    double weight;
}Person;

//vector.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "vector.h"

void readPerson(Vector *v)
    {
        readCSV(v->personArray);
    }

//person.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "person.h"

void initialize(Vector *v)
{
    v->sizeArray = 10;
    v->count = 0;
    v->personArray = (City*) malloc(v->sizeArray*sizeof(City));

}

void readCSV(Person* person)
    {
        FILE * fp;
        char line[1024];
        int i = 0;
        fp = fopen("mycsvfile.csv","r");
        while(fgets(line,sizeof(line),fp))
        {
            person->firstName = strtok(line,",");
            person->lastName = strtok(NULL,",");
            person->weight = atof(strtok(NULL,","));
            printf("%s %s %f\n",person->firstName, person->lastName, person->weight);
    }
    fclose(fp);
}

//main.c
#include<stdio.h>
#include<stdlib.h>
#include "vector.h"

int main()
    {
        Vector people;
        initialize(&people);
        readPerson(&people);
        return 0;
    }

字符串

s2j5cfk0

s2j5cfk01#

如下所示:

int readCSV(Person* person, FILE *fp){
    int state;
    char line[1024];
    if(state = fgets(line,sizeof(line), fp)){
        person->firstName = strdup(strtok(line,","));
        person->lastName  = strdup(strtok(NULL,","));
        person->weight    = atof(strtok(NULL,"\n"));
        printf("%s %s %f\n", person->firstName, person->lastName, person->weight);
    }
    return !!state;
}

void readPerson(Vector *v){
    //if(!v && !v->personArray)
    FILE *fp;
    int count = 0;
    if(NULL != (fp = fopen("mycsvfile.csv","r"))){
        while(readCSV(&v->personArray[count], fp)){
            if(++count == v->sizeArray){
                //expand array or stop reading
            }
        }
        v->count = count;
        fclose(fp);
    }
}

字符串

ep6jt1vc

ep6jt1vc2#

您的代码有两个主要问题:
1.在readCSV中,你有一个循环来读取CSV中的所有行,但是你阅读的所有数据总是存储在同一个(非数组)变量中。你需要传递一个Vector,而不是一个Person来读取CSV,并使用people->personArray[index].some_field来存储数据。
1.每次调用fgets()时,它都会覆盖第[1024]行。在再次调用fgets()之前,需要复制解析后的数据。为此,我使用了strdup。
在你的代码中,打印工作正常,因为你在调用fgets()之前打印了解析后的数据,但是问题2阻止了你将解析和打印分开。
这段代码修复了这两个问题,并将打印移到另一个函数中进行最小的编辑。我将person.c重命名为vector.c,因为那里的函数将矢量作为一个整体来处理,而不是单个人。

//person.h
#ifndef PERSON_H_
#define PERSON_H_

typedef struct
{
    const char *firstName, *lastName;
    double weight;
}Person;

#endif

//vector.h
#ifndef VECTOR_H_
#define VECTOR_H_
#include "person.h"

typedef struct
{
    Person *personArray;
    int sizeArray;
    int count;
}Vector;

extern void readCSV(Vector* people);
extern void printCSV(Vector* people);

#endif

//vector.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "vector.h"

void initialize(Vector *v)
{
    v->sizeArray = 10;
    v->count = 0;
    v->personArray = (Person*) malloc(v->sizeArray*sizeof(Person));

}

void readCSV(Vector* people)
{
    FILE * fp;
    char line[1024];
    int index = 0;
    fp = fopen("mycsvfile.csv","r");
    while(fgets(line,sizeof(line),fp))
    {
        people->personArray[index].firstName = strdup(strtok(line,","));
        people->personArray[index].lastName = strdup(strtok(NULL,","));
        people->personArray[index].weight = atof(strtok(NULL,","));
        ++index;
    }
    people->count = index;
    fclose(fp);
}

void printCSV(Vector* people)
{
    int i;
    for( i=0; i<people->count; ++i )
    {
            printf("%s %s %f\n",
                    people->personArray[i].firstName,
                    people->personArray[i].lastName,
                    people->personArray[i].weight);
    }
}

//main.c
#include<stdio.h>
#include<stdlib.h>
#include "vector.h"

int main()
{
        Vector people;
        initialize(&people);
        readCSV(&people);
        printCSV(&people);
        return 0;
}

字符串
这些代码是有效的,应该可以让你继续你的工作或任务。但是,为了成为值得生产状态的高质量代码,还有很多问题需要解决:
1.释放使用的内存。当不再使用时,需要释放用malloc、strdup等分配的内存。在这个小程序中,当main退出时,它将被C运行时释放,但当你的代码成为一个更大项目的一部分时,它将是一个重要的问题。最好现在就习惯它。
1.处理不受约束的输入。尝试使用包含11个条目的输入文件运行程序,它可能会崩溃或表现出未定义的行为。这是因为我们在初始化中分配了10个条目(Vector* v)。要解决这个问题,你可以使用一个可增长的数据类型,比如链表。或者你可以对文件进行两次解析;一旦丢弃数据以确定其具有多少条目,则分配该数组,然后分配第二个数组以实际读取和存储数据。
1.处理不正确的输入。您需要指定CSV文件的格式。是否允许空字段?最大行长度?并验证输入文件的正确性,如果没有解析而不是阅读不正确的输入,则会出现错误。如果文件不存在或无法读取,会发生什么情况?
1.使你的代码通用化。readCSV(Vector* people)应该变成readCSV(Vector*,const char* filename)。你可以在命令行中使用main(int argc,char* argv[])而不是使用常量字符串来读取文件名。
1.注意strtok()和fgets()的行为!使用fgets()读取的行(可能最后一个除外)将以“\n”结尾。在以“83.5\n”结尾的行中,最后一个标记将是“83.5\n”。请注意此处的回车符。这可能不是您想要的。在您的代码中,它可以工作,因为atof在“\n”处停止解析。如果您不想要\n您可以在您的内标识中使用strtok(line_or_NULL,“,\n”)
1.正确处理你的Vector状态。如果你调用readCSV()两次会发生什么?如果initialize()被调用了两次会发生什么?最安全的做法是检测它,因为它是一个快速的检查,如果调用不正确,程序会中止并返回错误代码,或者返回错误代码。
1.文档代码。每个函数的头,解释它的作用,参数和返回的内容。列出前置条件和后置条件是非常重要的,比如允许或不允许在同一个Vector上调用两次initialize。
1.单元测试。

相关问题