在处理空字段时解析字段的一种灵活而可靠的方法是使用简单的 start 和 end 指针和strcspn,其中@987654322 的字节@参数为",\n"。这样,您可以在每一行中遍历指针,使用 switch() 语句和指针算术将字段分隔为相应的结构成员。
在一个示例中一步一步地进行,您可以执行类似于以下的操作:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#define EMPTY -1 /* define value for number if empty-fields */
#define NMEMB 5 /* number of members in struct */
#define DATSZ 12 /* 10 not large enough for data string */
#define CNSZ 50 /* chars in city, name */
#define NLINE 128 /* number of struct (csv lines) */
#define MAXC 1024 /* number of chars in read buffer */
typedef struct RegDados {
int numberIns;
double grade;
char data[DATSZ];
char city[CNSZ];
char name[CNSZ];
} RegDados;
int main (int argc, char **argv) {
char buf[MAXC]; /* read buffer */
size_t ndx = 0; /* struct index */
/* initialize array of struct */
RegDados reg[NLINE] = {{ .numberIns = EMPTY, .grade = EMPTY }};
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
fgets (buf, MAXC, fp); /* read/discard header line */
/* read each line up to a max of NLINE lines */
while (ndx < NLINE && fgets (buf, MAXC, fp)) {
size_t n = 0; /* current field number */
char *p = buf, /* beginning pointer in field */
*ep = p + strcspn (p, ",\n"); /* end pointer in field */
while (*p && *p != '\n') { /* for each field */
switch (n) { /* switch on field number */
case 0:
if (ep > p && isdigit (*p)) { /* non-empty w/digit */
char *endptr; /* endptr for strtol */
long tmp = strtol (p, &endptr, 0); /* convert */
if (p == endptr) /* validate */
fputs ("error: no digits, numberIns.\n", stderr);
else if (errno)
fputs ("error: over/underflow.\n", stderr);
else if (tmp < INT_MIN || INT_MAX < tmp)
fputs ("error: exceeds range of int.\n", stderr);
else
reg[ndx].numberIns = tmp;
}
break;
case 1:
if (ep > p && isdigit (*p)) { /* non-empty w/digit */
char *endptr; /* endptr for strtod */
double tmp = strtod (p, &endptr); /* convert */
if (p == endptr) /* validate */
fputs ("error: no digits, numberIns.\n", stderr);
else if (errno)
fputs ("error: over/underflow.\n", stderr);
else
reg[ndx].grade = tmp;
}
break;
case 2:
if (ep > p && ep - p < DATSZ) { /* chars, will it fit? */
memcpy (reg[ndx].data, p, ep - p); /* copy chars */
reg[ndx].data[ep - p] = 0; /* nul-terminate */
}
break;
case 3:
if (ep > p && ep - p < CNSZ) { /* chars, will it fit? */
memcpy (reg[ndx].city, p, ep - p); /* copy chars */
reg[ndx].city[ep - p] = 0; /* nul-terminate */
}
break;
case 4:
if (ep > p && ep - p < CNSZ) { /* chars, will it fit? */
memcpy (reg[ndx].name, p, ep - p); /* copy chars */
reg[ndx].name[ep - p] = 0; /* nul-terminate */
}
break;
default: /* set default to indicate error */
fputs ("error: you shouldn't get here.\n", stderr);
break;
}
p = ++ep; /* set p to beginning of next field */
ep = p + strcspn (p, ",\n"); /* find end of field */
n++; /* increment field count */
}
ndx++; /* increment index */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++) /* output data */
printf ("%6d %6.1f %-12s %-20s %s\n", reg[i].numberIns,
reg[i].grade, reg[i].data, reg[i].city, reg[i].name);
return 0;
}
输入文件示例
$ cat dat/csvwempty.csv
nroInscricao,nota,data,cidade,nomeEscola
13893,353.9,26/11/2016,,FRANCISCO RIBEIRO CARRIL
13595,472.2,,Salgueiro,ALFREDO GUEDES
13894,614.4,28/11/2016,Recife,JOAO DE MOURA GUIMARAES
13880,403.2,29/11/2016,Fortaleza,ANTONIO DIAS PASCHOAL PR
13881,373.7,,Sao Jose da Tapera,DONIZETTI TAVARES DE LIM
13882,394.8,01/12/2016,Sao Bernardo do Cam,JUSTINO GOMES DE CASTRO
使用/输出示例
$ ./bin/parsecsv dat/csvwempty.csv
13893 353.9 26/11/2016 FRANCISCO RIBEIRO CARRIL
13595 472.2 Salgueiro ALFREDO GUEDES
13894 614.4 28/11/2016 Recife JOAO DE MOURA GUIMARAES
13880 403.2 29/11/2016 Fortaleza ANTONIO DIAS PASCHOAL PR
13881 373.7 Sao Jose da Tapera DONIZETTI TAVARES DE LIM
13882 394.8 01/12/2016 Sao Bernardo do Cam JUSTINO GOMES DE CASTRO
查看一下,如果您还有其他问题,请告诉我。