CSV文件高级解析

时间:2014-12-28 18:48:38

标签: c csv

解析.csv文件时遇到问题。我有struct world定义如下:

typedef struct world
{
    char worldName[30];
    int worldId;
    char *message;
    char **constellationArray;
    struct world *next;
} tWorld;

我有一个像这样设计的.csv文件(因此'c'代表'半冒号'):

worldId;worldName;message;constellationArray
1;K'tau;Planeta pod ochranou Freyra;Aquarius;Crater;Orion;Sagittarius;Cetus;Gemini;Earth
2;Martin's homeworld;Znicena;Aries;Sagittarius;Monoceros;Serpens;Caput;Scutum;Hydra;Earth
3;...

任务看似简单:编写方法loadWorlds(char *file)。加载文件并解析它。星座的数量无法保证。每一条新线都标志着一个新的世界,我必须创建这些世界的链接列表。我有一个粗略的想法,但我无法使它工作。我有一个名为tWorld *createWorld()的方法,它实现如下:

tWorld *createWorld() {
    tWorld *world;
    world = (*tWorld)malloc((sizeof(tWorld)));
    return world;
}

我必须在loadWorlds(char *文件)中使用此方法。另外,我必须将它们序列化到链表中:

if (*lastWorld == NULL){
    *lastWorld = nextWorld;
}else{
    (*actualWorld)->next = nextWorld;
}
*actualWorld = nextWorld;

但我不知道何时使用它。这是我loadWorlds(char *file)的粗略草图:

void loadWorlds(char *file)
{
    FILE *f;
    char text[30];
    char letter;
    tWorld *lastWorld = NULL, *actualWorld = NULL, *world;

    //f = fopen(file, "r");

    if(!(f = fopen(file, "r")))
    {
        printf("File does not exist! \n");
        while(!kbhit());
    }
    else
    {
        while(!(feof(f)) && (letter = fgetc(f))!= '\n')
        {

            if((znak = fgetc(f)) != ';')
            {

            }

        }
    }
}

我很感激任何想法让这项工作。

1 个答案:

答案 0 :(得分:0)

问题"如何解析此文件?...(另外我必须将它们序列化到链接列表中)" 在考虑时是一件非常重要的事情总共。您的"如何解析此文件?" 本身就是一个问题。第二部分,关于链表,是一个完全没有解释的完全独立的问题,尽管看来你指的是一个单一链表。有许多不同的方法可以解决这个问题,因为有葡萄酒的标签。我试图提供一个例子来帮助你。

在下面的示例中,我没有在worldName结构中创建一个静态字符数组tWorld,而是动态分配了所有其他字符串,我将worldName更改为character pointer也是如此。如果您必须使用static array of chars,那么可以轻松更改,但只要您分配剩余的字符串,就可以为worldName进行分配。

关于问题的parsing部分,您可以使用注释中标识的任意数量的库函数,或者您只需使用几个pointers并逐步解析每个字符串的每一行按要求。两种方法都没问题。使用简单指针的唯一好处(除了学习方面)是避免重复函数调用,在某些情况下可以更高效。从动态分配的行解析数据时,一个 note 确保保留缓冲区的起始地址,以确保可以正确跟踪和释放分配的内存。某些库函数会破坏原始缓冲区(即strtok等),如果您在没有以某种方式保留原始起始地址的情况下传递缓冲区本身就会导致有趣的错误。

下面的函数read_list_csv使用一对字符将从csv文件读取的每一行(实际为semi-colon separated值)解析为tWorld结构的每个成员指针解析输入行。 read_list_csv然后调用ins_node_end插入每个填充的&已将tWorld nodes分配到singularly-linked circular linked-list。对解析进行了注释以帮助解释逻辑,但总的来说,它只是将起始指针p设置为开头,然后使用结束指针ep检查行中的每个字符,直到出现分号{找到{1}},暂时将;设置为;(nul)并读取\0指向的字符串。临时p将替换为原始\n,并且该过程将从以下字符开始重复,直到该行已被完全解析。

问题的;部分涉及更多。许多linked-list只是部分解释并且通常等效正确,这很复杂。此外,linked-list examples几乎没有用,除非你可以添加它,从中读取,从中删除,并且在不泄漏记忆的情况下摆脱它。当您查看示例时,请注意有两个主要表单链接列表。 linked-list列表或HEAD/TAIL列表。两者都可以是circularsingularly链接。 doubly列表通常使用单独的指针来查找列表开头或HEAD/TAIL以及列表结尾或HEAD节点(通常设置为TAIL)。 NULL列表只是让结束节点circular指针指向列表的开头。两者都有它们的用途。 next列表的主要好处是,您可以遍历列表任何节点任何其他节点,无论您从列表中的哪个位置开始。 (因为没有circular,您可以从任何节点开始迭代所有节点。

以下示例为end-node。它提供了创建节点,将它们插入列表,计算节点,打印整个列表,从列表中删除节点以及删除列表的功能。重要的是,它释放了分配给列表的所有内存。

浏览示例的singularly linked circular list部分和示例中的parsing部分,如果您有任何疑问,请与我们联系。虽然列表实现应该相当可靠,但可能存在一些未发现的问题。代码后面显示了用于测试的数据文件以及示例输出。代码期望数据文件作为第一个参数,并且可选的(基于零的)节点作为第二个参数删除(默认值:节点2):

linked-list

输入测试数据文件:

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

#define MAXL 256
// #define wName 30

typedef struct world
{
    // char worldName[wName];
    char *worldName;
    int worldId;
    char *message;
    char **constellationArray;
    struct world *next;
} tWorld;

/* allocate & populate node */
tWorld *create_node (int wid, char *wnm, char *msg, char **ca);

/* insert node into list */
tWorld *ins_node_end (tWorld **list, int wid, char *wnm, char *msg, char **ca);

/* read data from file fname and add to list */
tWorld *read_list_csv (tWorld **list, char *fname);

/* return the number of nodes in list */
size_t getszlist (tWorld *list);

/* print all nodes in list */
void print_list (tWorld *list);

/* free memory allocated to tWorld list node */
void free_node (tWorld *node);

/* (zero-based) delete of nth node */
void delete_node (tWorld **list, int nth);

/* delete tWorld list & free allocated memory */
void delete_list (tWorld *list);

int main (int argc, char **argv)
{
    if (argc < 2) {
        fprintf (stderr, "error: insufficient input. Usage: %s <filename> [del_row]\n", argv[0]);
        return 1;
    }

    char *fname = argv[1];
    tWorld *myworld = NULL;             /* create pointer to struct world   */

    read_list_csv (&myworld, fname);    /* read fname and fill linked list  */

    printf ("\n Read '%zd' records from file: %s\n\n", getszlist (myworld), fname);

    print_list (myworld);               /* simple routine to print list     */

    int nth = (argc > 2) ? atoi (argv[2]) : 2;
    printf ("\n Deleting node: %d\n\n", nth);
    delete_node (&myworld, nth);        /* delete a node from the list      */

    print_list (myworld);               /* simple routine to print list     */

    delete_list (myworld);              /* free memory allocated to list    */

    return 0;
}

/* allocate & populate node */
tWorld *create_node (int wid, char *wnm, char *msg, char **ca) 
{
    tWorld *node = NULL;

    node = malloc (sizeof *node);
    if (!node) return NULL;

    node-> worldId = wid;
    node-> worldName = wnm;
    node-> message = msg;
    node-> constellationArray = ca;

    return node;
}

/* insert node into list */
tWorld *ins_node_end (tWorld **list, int wid, char *wnm, char *msg, char **ca) 
{
    tWorld *node = NULL;
    if (!(node = create_node (wid, wnm, msg, ca))) return NULL;


    if (!*list) {    /* if empty, create first node */
        node-> next = node;
        *list = node;
    } else {         /* insert as new end node */
        if (*list == (*list)-> next) { /* second node, no need to iterate */
            (*list)-> next = node; 
        }
        else                           /* iterate to end node & insert    */
        {
            tWorld *iter = *list;      /* second copy to iterate list     */
            for (; iter->next != *list; iter = iter->next) ;
            iter-> next = node;        /* insert node at end of list      */
        }
        node-> next = *list;           /* set next pointer to list start  */
    }

    return *list;   /* provides return as confirmation  */
}

/* read list from file fname and add to list */
tWorld *read_list_csv (tWorld **list, char *fname)
{
    FILE *fp = fopen (fname, "r");
    if (!fp) {
        fprintf (stderr, "%s() error: file open failed for '%s'\n", __func__, fname);
        return NULL;
    }

    /* allocate and initialize all variables */
    char *line = calloc (MAXL, sizeof *line);
    char *p = NULL;
    char *ep = NULL;
    char *wnm = NULL;
    int wid = 0;
    int lcnt = 0;
    char *msg = NULL; 
    char **ca = NULL;
    size_t idx = 0;

    while (fgets (line, MAXL, fp))      /* for each line in file    */
    {
        if (lcnt++ == 0) continue;      /* skip header row          */

        p = line;
        idx = 0;
        ep = p;
        size_t len = strlen (line);     /* get line length          */
        if (line[len-1] == '\n')        /* strip newline from end   */
            line[--len] = 0;

        while (*ep != ';') ep++;        /* parse worldId            */
        *ep = 0;
        wid = atoi (p);
        *ep++ = ';';
        p = ep;

        while (*ep != ';') ep++;        /* parse worldName          */
        *ep = 0;
        wnm = strdup (p);
        *ep++ = ';';
        p = ep;

        while (*ep != ';') ep++;        /* parse message            */
        *ep = 0;
        msg = strdup (p);
        *ep++ = ';';
        p = ep;

        ca = calloc (MAXL, sizeof *ca); /* allocate constellationArray */
        if (!ca) {
            fprintf (stderr, "%s() error allocation failed for 'ca'.\n", __func__);
            return NULL;
        }
        while (*ep)                     /* parse ca array elements  */
        {
            if (*ep == ';')
            {
                *ep = 0;
                ca[idx++] = strdup (p);
                *ep = ';';
                p = ep + 1;
                /* if (idx == MAXL) reallocate ca */
            } 
            ep++;
        }
        if (*p) ca[idx++] = strdup (p); /* add last element in line */

        ins_node_end (list, wid, wnm, msg, ca); /* add to list      */
    }

    /* close file & free line */
    if (fp) fclose (fp);
    if (line) free (line);

    return *list;
}

/* return the number of nodes in list */
size_t getszlist (tWorld *list) {

    const tWorld *iter = list;  /* pointer to iterate list  */
    register int cnt = 0;

    if (iter ==  NULL) {
        fprintf (stdout,"%s(), The list is empty\n",__func__);
        return 0;
    }

    for (; iter; iter = (iter->next != list ? iter->next : NULL)) {
        cnt++;
    }
    return cnt;
}

/* print all nodes in list */
void print_list (tWorld *list) {

    const tWorld *iter = list;  /* pointer to iterate list  */
    register int idx = 0;
    char *stub = " ";

    if (iter ==  NULL) {
        fprintf (stdout,"%s(), The list is empty\n",__func__);
        return;
    }

    for (; iter; iter = (iter->next != list ? iter->next : NULL)) {
        printf (" %2d  %-20s  %-20s\n", 
                iter-> worldId, iter-> worldName, iter-> message);
        idx = 0;
        while ((iter-> constellationArray)[idx])
            printf ("%38s %s\n", stub, (iter-> constellationArray)[idx++]);
    }
}

/* free memory allocated to tWorld list node */
void free_node (tWorld *node)
{
    if (!node) return;

    register int i = 0;

    if (node-> worldName) free (node-> worldName);
    if (node-> message) free (node-> message);
    while (node-> constellationArray[i])
        free (node-> constellationArray[i++]);
    if (node-> constellationArray)
        free (node-> constellationArray);

    free (node);
}

/* (zero-based) delete of nth node */
void delete_node (tWorld **list, int nth)
{
    /* test that list exists */
    if (!*list) {
        fprintf (stdout,"%s(), The list is empty\n",__func__);
        return;
    }

    /* get list size */
    int szlist = getszlist (*list);

    /* validate node to delete */
    if (nth >= szlist || nth < 0) {
        fprintf (stderr, "%s(), error: delete out of range (%d). allowed: (0 <= nth <= %d)\n", 
                __func__, nth, szlist-1);
        return;
    }

    /* create node pointers */
    tWorld *victim = *list;
    tWorld *prior = victim;

    /* if nth 0, prior is last, otherwise node before victim */
    if (nth == 0) {
        for (; prior->next != *list; prior = prior->next) ;
    } else {
        while (nth-- && victim-> next != *list) {
            prior = victim;
            victim = victim-> next;
        }
    }

    /* non-self-reference node, rewire next */
    if (victim != victim->next) {
        prior-> next = victim-> next;

        /* if deleting node 0, change list pointer address */
        if (victim == *list)
            *list = victim->next;
    } else {  /* if self-referenced, last node, delete list */
        *list = NULL;
    }

    free_node (victim);  /* free memory associated with node */
}

/* delete tWorld list */
void delete_list (tWorld *list)
{
    if (!list) return;

    tWorld *iter = list;  /* pointer to iterate list  */

    for (; iter; iter = (iter->next != list ? iter->next : NULL))
        if (iter) free_node (iter);
}

<强>输出:

$ cat dat/struct.csv

worldId;worldName;message;constellationArray
1;K'tau;Planeta pod ochranou Freyra;Aquarius;Crater;Orion;Sagittarius;Cetus;Gemini;Earth
2;Martin's homeworld;Znicena;Aries;Sagittarius;Monoceros;Serpens;Caput;Scutum;Hydra;Earth
3;Martin's homeworld2;Znicena2;Aries2;Sagittarius2;Monoceros2;Serpens2;Caput2;Scutum2;Hydra2;Earth2
4;Martin's homeworld3;Znicena3;Aries3;Sagittarius3;Monoceros3;Serpens3;Caput3;Scutum3;Hydra3;Earth3