如何在 C 中解析带引号的 .csv 文件

How to parse a .csv file with quotation marks in C

假设我正在尝试解析的字符串读取

"Smith, John",Data1,Data2,Data3

我还可以得到读作

的行

Dave, Data1,Data2,Data3

所以我有 if 语句

line 是文件中 fgets() 的文本行,但我认为行得通

剩下的我已经苦苦挣扎了大约一个小时了。我正在尝试重新格式化 "Smith, John" 使其成为 John Smith,然后将其分配给 recTemp.artist

if (line[0] == '\"') {
    //Read the last name, first name",
    char lastTemp[30] = "";
    char firstTemp[30] = "";
    strcpy(lastTemp , strtok(line, ", "));
    strcpy(firstTemp, strtok(NULL, "\","));
    char * t;
    t = strstr(lastTemp, "\"");
    strcpy(t, " ");
    //Concatenate each string assign to the artist value
    strcat(firstTemp, lastTemp);
    strcpy(recTemp.artist, firstTemp);
}

我认为错误来自 strstr 调用或紧随其后的 strcpy,但我不确定

谢谢!

回答你的问题:

"I'm trying to reformat the "Smith, John" so its John Smith"

如果不使用正则表达式从字符串中提取引号,我会执行以下操作,

#include <iostream>
#include <cstring>
#include <stdio.h>

int main() {
    char line[100] = "\"Smith,John\",Data1,Data2,Data3";
    // fgets(line, 100, stdin);
    char* name = strtok(line, "\"");
    char *substring2 = strtok(NULL, "\"");
    char* LastName = strtok(name, ",");
    char* FirstName = strtok(NULL, ",");

    char result[100];
    strcpy(result, FirstName);
    strcat(result, ",");
    strcat(result, LastName);
    strcat(result, substring2);
    printf("%s",result);

}

产生输出:

John,Smith,Data1,Data2,Data3

如果你想避免用 strtok 改变 line,你可以简单地使用指针算法将 "first last" 复制到 recTemp.artist,或者复制 "name" ] 在第一个字段中没有引号的情况下。这只是您可以采用的另一种避免修改原始字符串的方法。 (以及使用指针的健康锻炼)

在引号存在的情况下,您可以将指针 (p) 设置为 line + 1 并使用 strstr 查找子字符串 "\"," 设置结束指向收盘价的指针 (endptr)。然后,您可以使用 ','p 上调用 strchar 以定位 last, first 之间的逗号并设置另一个指针以前进到名字的开头(firstp ).一旦 firstp 指向名字的开头,您可以将名字 memcpy 指向 recTemp.artist,添加一个 space,然后复制姓氏,接下来是 nul 终止。

在没有引号的情况下,只需要用strchr定位到','字段分隔符,然后调用memcpy,然后nul-terminate。

一个简短的例子是:

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

typedef struct {
    char artist[64];
} rec_t;

int main (void) {
#ifndef NOQUOTE    
    char line[] = "\"Smith, John\",Data1,Data2,Data3";
#else
    char line[] = "Dave,Data1,Data2,Data3";
#endif
    rec_t recTemp;

    if (*line == '\"') {    /* if double-quotes are present */
        char *p = line + 1, *endptr, *sep;    /* ptr, endptr & sep */
        if (!(endptr = strstr (p, "\","))) {  /* find close quote, validate */
            fputs ("error: invalid line format.\n", stderr);
            /* handle error as needed, e.g. */
            return 1;
        }

        if ((sep = strchr (p, ','))) {        /* locate ',' in last, first */
            char *firstp = sep + 1;           /* set firstp to next char */
            while (*firstp && *firstp == ' ') /* skip any leading spaces */
                firstp++;
            memcpy (recTemp.artist, firstp, endptr - firstp); /* copy first */
            endptr = recTemp.artist + (endptr-firstp);  /* set endptr after */
            *endptr++ = ' ';                  /* add a space */
            memcpy (endptr, p, sep - p);      /* copy last */
            *(endptr + (sep - p)) = 0;        /* nul-terminate */
        }
    }
    else {  /* otherwise - name without quotes */
        char *sep = strchr (line, ',');       /* find field seperator */
        if (!sep) {
            fputs ("error: invalid line format.\n", stderr);
            /* handle error as needed, e.g. */
            return 1;
        }
        memcpy (recTemp.artist, line, (sep - line));  /* copy name */
        *(recTemp.artist + (sep - line)) = 0; /* nul-terminate */
    }

    printf ("recTemp.artist: '%s'\n", recTemp.artist);
}

例子Use/Output

$ ./bin/rectmp
recTemp.artist: 'John Smith'

使用 -DNOQUOTE 编译的不带引号的名称大小写:

$ ./bin/rectmpnq
recTemp.artist: 'Dave'

无论您使用 strtok 还是向下走几步 line,都可以。如果你想保持 line 不变,那么要么在使用 strtok 对副本进行操作之前制作一个副本,要么只使用指针算法。您可以转储在这两种方法上生成的程序集,以查看您的编译器是否在这两种方法之间提供了优化优势。在宏伟的计划中,差异可以忽略不计。