首页 > 解决方案 > 需要帮助从 .csv 文件 C 中解析数据

问题描述

我有以下.csv文件,其中包含有关歌曲、艺术家、发行年份(如果指定)和收听次数的信息:

看猫被毒药拖进来的是什么,看猫被毒药拖进来的是什么,1,0,1,0
Nothin' But A Good Time,Poison,1988,Nothing But A Good Time by Poison,1,1,21,21
相信的东西,毒药,1990,相信的东西 毒药,1,1,1,1
对我说脏话,毒药,1978,用毒药对我说脏话,1,1,1,1
咸狗,Procol Harum,1969,Procol Harum 的咸狗,1,1,1,1
一种更白的苍白阴影,Procol Harum,1967,Procol Harum 的一种更白的苍白阴影,1,1,3,3
模糊,泥坑,2001,泥坑模糊,1,1,1,1
Amie,Pure Prairie League,,Amie by Pure Prairie League,1,0,4,0
另一只咬尘埃,女王,1980,另一只咬尘埃由皇后,1,1,102,102
自行车比赛,女王,1978,女王自行车比赛,1,1,3,3
吻遍你,吻,1978,吻遍你,1,1,5,5

文件名和所需年份应作为命令行参数给出,程序应打印该特定年份的所有歌曲。

例如:./a.out music.csv 1978

输出:

Talk dirty to me
Bicycle Race
Kiss You All Over

代码:

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

#define MAX 300

typedef struct {
    char song[101], *artist, *line;
    long int year;
} music;

int checkYear(char *word)
{
    for (int i = 0; i < strlen(word); i++) {
        if (!isdigit(word[i]))
            return 0;
    }
    return 1;
}

int main(int argc, char **argv)
{
    FILE *fin = fopen(argv[1], "r");
    if (!fin)
    {
        printf("Error opening the file.\n");
        return 1;
    }
    char buf[MAX];
    //int nLines = 0; //count the number of lines
    //music *array = NULL;
    while( fgets(buf, MAX, fin))
    {
        buf[strcspn(buf, "\n")] = '\0';  // strip the trailing newline
        char *word = strtok(buf, ",");
        while (word)
        {
            //printf("Word is : %s\n", word);
            if (checkYear(word))
            {
                //printf("Year : %s\n", word);
                music *array = (music *)malloc(sizeof(music));
                char *p;
                array->year = strtol(word, &p, 10);
                if (array->year == atoi(argv[2]))
                {
                    //printf("Year : %ld\t%d\n", array->year, atoi(argv[2]));
                    if (scanf("%100[^,]", array->song) == 1)
                    {
                        printf("Song : %s\n", array->song);
                    }
                }
            }
            word = strtok(NULL, ",");
        }
    }
    //printf("I've read %d lines\n", nLines);
    fclose(fin);
    return 0;
}

到目前为止,一切顺利,我可以从每一行中提取指定的年份,但现在我只需要从这些行中打印出歌曲的名称(行上的第一个标记)。我考虑过使用scanf("%[^,]")读取和打印所有内容直到第一个逗号,但它只是陷入了无限循环。你能给我一个想法吗?提前致谢!

标签: ccsv

解决方案


代码中存在多个问题:

  • 您没有检查是否在命令行上传递了足够的参数,如果没有,可能会调用未定义的行为。
  • 您不需要分配music结构:您可以只解析前 3 个字段,检查年份并直接输出歌曲名称。
  • strtok()不适合从csv文件中拆分字段,因为它将分隔符序列视为单个分隔符,这是不正确的,并且如果某些字段为空,则会导致解析无效。
  • sscanf("%[^,]", ...)将无法转换空字段。

要从csv行中拆分字段,我建议您使用一个效用函数,该函数的行为类似于strtok_r()但为csv行量身定制。一个简单的版本将停止,\n用空字节替换它们,返回初始指针并更新下一个字段的指针。更高级的版本也可以处理引号。

这是修改后的版本:

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

#define MAX 300

char *get_field(char **pp) {
    char *p, *start;
    for (p = start = *pp; *p; p++) {
        if (*p == ',' || *p == '\n') {
            *p++ = '\0';
            break;
        }
    }
    *pp = p;
    return start;
}
     
int main(int argc, char *argv[]) {
    char buf[MAX];
    FILE *fin;
    char *filename;
    char *select_year;

    if (argc < 3) {
        printf("Missing arguments\n");
        return 1;
    }
    filename = argv[1];
    select_year = argv[2];
    fin = fopen(filename, "r");
    if (!fin) {
        printf("Error opening the file %s.\n", filename);
        return 1;
    }

    while (fgets(buf, sizeof buf, fin)) {
        char *p = buf;
        char *song = get_field(&p);
        char *artist = get_field(&p);
        char *year = get_field(&p);
        if (!strcmp(year, target_year)) {
            printf("%s\n", song);
        }
    }
    fclose(fin);
    return 0;
}

推荐阅读