首页 > 解决方案 > 将 CSV 文件读入结构的动态数组 C

问题描述

我对C相当陌生。我正在尝试读取.CSV文件,然后解析每一行,然后将数据存储在指向结构的指针的动态数组中。不幸的是,我在实现中的某个地方出错了,这导致了无限循环。

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

typedef struct dataSet {
    char ID;
    char postcode;
    int population;
    char contact;
    double x;
    double y;
}data;

int main(int argc, char* argv[]) {

    char line[100] = "";
    int count = 0;
    int each = 0;
    data *allData = NULL;
    data *temp = NULL;

    FILE *file = fopen("dataset.csv", "r");
    if (file == NULL)
    {
        printf("Error! File null");
        return 1;
    }

    while (fgets(line, sizeof line, file))
    {
        if(NULL == (temp = realloc(allData, sizeof(*allData) * (count + 1))))
        {
            fprintf(stderr, "realloc problem\n");
            fclose(file);
            free(allData);
            return 0;
        }

        allData = temp;
        if (6 == scanf(line, "%s, %s, %d, %s, %lf, %lf",
                        &allData[count].ID,
                        &allData[count].postcode,
                        &allData[count].population,
                        &allData[count].contact,
                        &allData[count].x,
                        &allData[count].y)) {
            count++;
        }
        else {
            printf("Problem with data\n");
        }
    }

    fclose(file);

    for (each = 0; each < count; each++)
    {
        printf("%s, %s, %d, %s, %lf, %lf\n",
        &allData[count].ID,
        &allData[count].postcode,
        &allData[count].population,
        &allData[count].contact,
        &allData[count].x,
        &allData[count].y);
    }

    free(allData);

    return 0;
}

任何帮助或提示将不胜感激。

标签: arraysccsvstruct

解决方案


[s]scanf()是一个讨厌的功能。一旦失败,您就没有足够的控制权。问题是:条件太多:输入可能不正确,或者目标不够大。即使用 阅读完整的行fgets()并在之后对其进行解析,也只会让您跳过完整的行;另外:行缓冲区大多是固定大小的,并且fgets()可以读取不完整的行。保持完全控制的一种方法是基于字符的读取。这可能意味着有限状态机。

一个更简单的阅读器(使用零状态机)可能是:


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

struct omg {
        char o;
        int m;
        char g[11];
        };

struct wtf {
        unsigned size;
        unsigned used;
        struct omg *array;
        };

#define INITIAL_SIZE 7

struct wtf read_stuff(char *name)
{
FILE *fp;
unsigned icol,irec,len;
char buff[123];
struct wtf this = {0,0,NULL};

fp = fopen(name, "rb" );
if (!fp) return this;

for (icol=irec=len=0;   ; ) {
        int ch;
        if (this.used >= this.size) {
                size_t newsize;
                struct omg *tmp;

                newsize = this.size? this.size*2: INITIAL_SIZE;
                fprintf(stderr, "Realloc(%zu)\n", newsize);
                tmp = realloc(this.array, sizeof *this.array * newsize);
                this.array = tmp;
                this.size = newsize;
                }

        ch = getc(fp);
        switch(ch) {
        case '\r' : continue;

                /* End of field or record: terminate buffer */
#if 0
        case ',' :
#else
        case '\t' :
#endif

        case '\n' :
                buff[len] = 0;
                break;
        case EOF :
                goto done;

                /* Normal character: assign to buffer
                ** You may want to report too long fields here
                 */
        default:
                if (len >= sizeof buff -2) continue;
                buff[len++] = ch;
                continue;
                }

                /* When we arrive here, we have a new field. Let's process it ...*/
        switch (icol) {
        case 0: /* Assign first field here from buff[], (dont forget to check len!) */
                this.array[this.used].o = buff[0];
                break;

        case 1: /* Assign second field from buff[], this may need some additional checks
                ** You may want to avoid sscanf() here ...
                */
                sscanf(buff, "%d", &this.array[this.used].m );
                break;

        case 2: /* Assign third field from buff[] */
                if (len >= sizeof this.array[this.used].g)
                len = sizeof this.array[this.used].g -1;
                memcpy (this.array[this.used].g, buff, len);
                this.array[this.used].g[len] = 0;
                break;

        default: /* Ignore excess fields
                 ** You may want to report hem.
                 */
                break;
                }

                /* Do some bookkeeping */
        len = 0;
        if(ch == '\n') {
                /* You may want to check if icol==2, here */
                icol=0; irec++; this.used++;
                }
        else icol++;
        }
done:
fclose(fp);
        /* You could do a final realloc() here */

return this;
}

int main(int argc, char **argv)
{
struct wtf result;
unsigned idx;

result = read_stuff(argv[1] );
fprintf(stderr, "Result=%u/%u\n", result.used,result.size);

for (idx=0; idx < result.used; idx++) {
        printf("%c %d %s\n"
                , result.array[idx].o
                , result.array[idx].m
                , result.array[idx].g);
        if (idx >= 10) break;
        }

return 0;
}

推荐阅读