从.csv文件中读取数据以附加到c中的2D数组中

时间:2018-02-16 23:45:42

标签: c

for (int i = 0; i < 4; i++) {
    for (int j = 0; j <4; i++) {
        while (c != EOF)
            token = strtok((fgets(token,5,fp)), delim);
    }
}

大家好,我是C的新手,我获得了一个项目来获取csv文件并计算每列中平均值的数量。现在我正试图用逗号解析这些行。我发现函数[strtok],但我肯定是错误地实现它。我有csv文件中的行数和列数,我只需要帮助弄清楚如何用“,”解析每一行,并将这些值放入2D数组中。上面是我将用于将值附加到数组的当前代码,但我不断收到“分段错误”。任何帮助将不胜感激。

这是函数的完整代码。我包括stdio.h和stdlib.h:

void main() {
    char *strcat(char *dest, const char *src);
    char *strtok(char *str, const char *delim);

    char *file_name = "test.txt";

    FILE *fp = fopen(file_name, "r");

    int array[4][4];
    //int array1 [2] = {1, 3};

    int counter = 0;
    char *token = " ";
    const char *delim = (const char *)',';

    char c = fgetc(fp);

    for (int i = 0; i < 4; i++) {
        token = "";
        for (int j = 0; j <4; i++) {
            while (c != EOF)
                token = strtok((fgets(token,5,fp)), delim);
        }
    }
    fclose(fp);
}

示例输入将是这样的:

10,20,30,60
40,50,60,70
70,80,90,80
100,110,120,70

1 个答案:

答案 0 :(得分:0)

是的,你是对的,你错误地使用了strtok

我要做的第一件事就是阅读每一行并使用解析该行 strtok,就像这样:

char line[1024];

const char *delim=",\n";

while(fgets(line, sizeof line, fp))
{
    char *token = strtok(line, delim);

    do {
        printf("token: %s\n", token);
    } while(token = strtok(NULL, delim));
}

strtok要求必须调用strtok的所有后续调用 NULL。当没有更多令牌可以找到时,strtok将返回NULL,通常是 已达到行尾。请注意,我在中添加了换行符 分隔符论点。当目标缓冲区足够大fgets写入时 新线也是。将换行符放在分隔符列表中是个好方法 因为strtok会为你删除换行符。

上面的代码为您提供了一种获取csv的每个单元格的方法,作为字符串。您 必须自己转换价值观。如果csv,这是棘手的一点 包含空格,引号等,你需要不同的策略来解析 正确的细胞价值。您可以使用strtol&amp;等功能。朋友哪个 允许你从错误中恢复,但它们不是防弹,会有 他们失败的情况。

一个简单的例子是:

char line[1024];

const char *delim=",\n";

while(fgets(line, sizeof line, fp))
{
    char *token = strtok(line, delim);

    do {
        int val;
        if(sscanf(token, "%d", &n) != 1)
            fprintf(stderr, "'%s' is not a number!\n", token);
        else
            printf("number found: %d\n", val);
    } while(token = strtok(NULL, delim));
}

请注意,这并未涵盖所有情况,例如引号中的单元格。

要做的最后一件事是存储值。一种方法是 为指向int数组的指针分配内存并重新分配内存 每个细胞。这里的问题再次出现在csv文件中,有时它们有 格式错误,某些行将为空或某些行将具有更多或更少 列比其他行,这可能是棘手的。在这一点上,这将是一个很好的 想要使用库来解析csv。

以下代码将假设csv格式正确且数量为 所有行的列始终相同,并且没有行超过1023 长字符。当*cols为0时,我会根据基数计算列数 第一行。如果其他行具有较少的列,则所有剩余值将为0 (因为calloc将新分配的内存设置为0)。如果有更多 colmuns比第一行,这列将被忽略:

int **parse_csv(const char *filename, size_t *rows, size_t *cols)
{
    if(filename == NULL || rows == NULL || cols == NULL)
        return NULL;

    FILE *fp = fopen(filename, "r");

    if(fp == NULL)
        return NULL;

    int **csv = NULL, **tmp;

    *rows = 0;
    *cols = 0;

    char line[1024];
    char *token;
    char *delim = ",\n";

    while(fgets(line, sizeof line, fp))
    {
        tmp = realloc(csv, (*rows + 1) * sizeof *csv);
        if(tmp == NULL)
            return csv; // return all parsed rows so far

        csv = tmp;

        if(*cols == 0)
        {
            // calculating number of rows
            char copy[1024];
            strcpy(copy, line);

            token = strtok(copy, delim);

            do {
                (*cols)++;
            } while((token = strtok(NULL, delim)));
        }

        int *row = calloc(*cols, sizeof *row);

        if(row == NULL)
        {
            if(*rows == 0)
            {
                free(csv);
                return NULL;
            }

            return csv; // return all parsed rows so far
        }

        // increment rows count
        (*rows)++;

        size_t idx = 0;

        token = strtok(line, delim);

        do {
            if(sscanf(token, "%d", row + idx) != 1)
                row[idx] = 0; // in case the conversion fails,
                              // just to make sure to have a defined value
                              // in the cell

            idx++;
        } while((token = strtok(NULL, delim)) && idx < *cols);

        csv[*rows - 1] = row;
    }

    fclose(fp);
    return csv;
}

void free_csv(int **csv, size_t rows)
{
    if(csv == NULL)
        return;

    for(size_t i = 0; i < rows; ++i)
        free(csv[i]);

    free(csv);
}

现在你可以像这样解析它:

size_t cols, rows;
int **csv = parse_csv("file.csv", &rows, &cols);

if(csv == NULL)
{
    // error handling...
    // do not continue
}

...

free_csv(csv, rows);

现在csv[3][4]会给你第3行第4列的单元格(从0开始)。

修改

我从你的代码中注意到的事情:

void main()错了。 main应该只有以下原型之一:

  • int main(void);
  • int main(int argc, char **argv);
  • int main(int argc, char *argv[]);

另:

int main(void)
{
    char *strcat(char *dest, const char *src);
    char *strtok(char *str, const char *delim);
    ...

}

不要把它放在main函数中,把它放在外面,也有标准 这个头文件。在这种情况下,请包含string.h

#include <string.h>

int main(void)
{
    ...
}

另一个

const char *delim = (const char *)',';

这是错的,它就像试图卖苹果并称之为橙色。 ','char类型的单个字符。它的值为44.它与...相同 这样做的:

const char *delim = (const char*) 44;

您正在设置delim应指向44的地址。

你必须使用双引号:

const char *delim = ",";

请注意,'x'"x"不一样。 'x'是120(见ASCII),它是 单个字符。 "x"是一个字符串文字,它返回一个指向开头的指针 一系列以'\0'结尾的字符结尾的字符,也就是a 串。这些在C中根本不同。