c - 需要帮助从 .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("%[^,]")
读取和打印所有内容直到第一个逗号,但它只是陷入了无限循环。你能给我一个想法吗?提前致谢!
解决方案
代码中存在多个问题:
- 您没有检查是否在命令行上传递了足够的参数,如果没有,可能会调用未定义的行为。
- 您不需要分配
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;
}
推荐阅读
- data-structures - 这是一个有效的二叉树吗?
- html - 为什么我在这个网站上的移动菜单可以在浏览器上运行,但不能在实际的移动设备上运行?
- c# - 使用 C# 的 Azure Blob 存储“找不到方法”
- python - 为什么 psycopg2 INSERT 需要这么长时间才能循环运行,我该如何加快速度?
- sql - 将“名称”列中具有相同值的所有行合并为一行
- c++ - 是否可以使用 C++/WinRT 创建 Windows 服务应用程序?
- php - 一次循环一个结果
- oracle - 即使列有前缀,“列定义不明确的错误”?
- python - Pyinstaller 和 Kivy - exe 无法启动脚本
- c# - 推荐使用 Cofoundry cms 的类型/文件结构?