Quelque 200 lignes de code plus tard ... et en utilisant une version légèrement modifiée de votre fichier de données (notez que la deuxième ligne d'en-tête dans l'original est manquant tous les virgules):
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
** Example data:
**
** #station name
** Station Name : A1
** #octan of fuel,full service price,self service price
** Octan95,6.54,6.40
** Octan98,8.30,8.15
** #carNum,Octan,numOfLiters,Kind of service
** 22-334-55,95,31.3,FullService
** 22-334-55,95,31.3,SelfService
** 11-444-77,95,12,FullService
** 11-444-77,95,44.1,FullService
** 11-444-77,95,11.22,SelfService
**
** - Header lines are followed by one or more data lines
** - Number of fields in header matches number of fields in each data line
** - Commas separate fields and do not appear within fields (not full CSV)
*/
/* A Line structure holds the fields for one line */
typedef struct Line
{
size_t num_fields;
char **fields;
} Line;
/* A Section structure holds the header line and the set of data lines */
typedef struct Section
{
size_t num_rows;
size_t num_cols;
Line header;
Line *lines; /* Array of lines - num_rows entries in array */
} Section;
/* An Info structure holds all the sections for a single file */
typedef struct Info
{
size_t num_sections;
Section *sections;
} Info;
static void err_exit(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(1);
}
static void *xrealloc(void *old_data, size_t nbytes)
{
void *new_data = realloc(old_data, nbytes);
if (new_data == 0)
err_exit("Out of memory!\n");
return new_data;
}
static void *xmalloc(size_t nbytes)
{
void *new_data = malloc(nbytes);
if (new_data == 0)
err_exit("Out of memory!\n");
return new_data;
}
/* Duplicate a string of given length (excluding NUL) */
static char *xstrndup(const char *str, size_t len)
{
char *new_data = xmalloc(len+1);
memmove(new_data, str, len);
new_data[len] = '\0';
return new_data;
}
static void dump_line(FILE *fp, const Line * const line)
{
size_t i;
const char *pad = "";
for (i = 0; i < line->num_fields; i++)
{
fprintf(fp, "%s%*s", pad, 1, line->fields[i]);
pad = " ";
}
fputc('\n', fp);
}
static void dump_section(FILE *fp, const char *tag, const Section * const section)
{
if (tag != 0)
fprintf(fp, "Dump Section: %s\n", tag);
fprintf(fp, "Number of columns: %zd\n", section->num_cols);
fprintf(fp, "Number of lines: %zd\n", section->num_rows);
dump_line(fp, §ion->header);
for (size_t i = 0; i < section->num_rows; i++)
dump_line(fp, §ion->lines[i]);
}
static void dump_info(FILE *fp, const char *tag, const Info * const info)
{
size_t i;
fprintf(fp, "Dump Information: %s\n", tag);
fprintf(fp, "Number of sections: %zd\n", info->num_sections);
for (i = 0; i < info->num_sections; i++)
{
char title[20];
snprintf(title, sizeof(title), "%d", i+1);
dump_section(fp, title, &info->sections[i]);
}
fprintf(fp, "End of Information Dump\n");
}
static int num_fields(const char *buffer)
{
size_t posn = 0;
size_t next;
int count = 0;
while ((next = strcspn(buffer + posn, ",\n")) > 0)
{
count++;
if (buffer[posn+next] == '\n')
break;
posn += next + 1;
}
return count;
}
static void set_line(Line *line, int nfields, const char *buffer)
{
size_t posn = 0;
line->num_fields = nfields;
line->fields = xmalloc(nfields * sizeof(*line->fields));
for (int i = 0; i < nfields; i++)
{
size_t next = strcspn(buffer+posn, ",\n");
line->fields[i] = xstrndup(buffer+posn, next);
if (buffer[posn+next] == '\n')
{
if (i != nfields - 1)
err_exit("Internal error: field count mismatch\n");
break;
}
posn += next + 1;
}
}
static int add_section(Info *info, char *buffer)
{
int nfields = num_fields(buffer);
int nsections = info->num_sections + 1;
info->sections = xrealloc(info->sections, nsections * sizeof(*info->sections));
info->num_sections = nsections;
Section *new_section = &info->sections[nsections-1];
new_section->num_cols = nfields;
new_section->num_rows = 0;
set_line(&new_section->header, nfields, buffer);
new_section->lines = 0;
return nfields;
}
/* Beware - very compact code! */
static void add_line_to_section(Section *section, const char *buffer, int nfields)
{
section->lines = xrealloc(section->lines, (section->num_rows + 1) * sizeof(*section->lines));
set_line(§ion->lines[section->num_rows++], nfields, buffer);
}
static int peek(FILE *fp)
{
int c;
if ((c = getc(fp)) != EOF)
ungetc(c, fp);
return c;
}
static void read_info(FILE *fp, Info *info)
{
char buffer[1024];
while (fgets(buffer, sizeof(buffer), fp) != 0)
{
if (*buffer != '#')
err_exit("Format error: expected line beginning '#' (got '%.*s')\n",
10, buffer);
int nfields = add_section(info, buffer+1);
int c;
Section *cursect = &info->sections[info->num_sections-1];
while ((c = peek(fp)) != EOF && c != '#')
{
if (fgets(buffer, sizeof(buffer), fp) != 0)
{
int lfields = num_fields(buffer);
if (lfields != nfields)
err_exit("Mismatch in number of fields (got %d, wanted %) at '%*s'\n",
lfields, nfields, 20, buffer);
add_line_to_section(cursect, buffer, nfields);
}
}
}
}
int main(int argc, char **argv)
{
int i;
Info info = { 0, 0 };
for (i = 1; i < argc; i++)
{
FILE *fp;
if ((fp = fopen(argv[i], "r")) != 0)
{
read_info(fp, &info);
dump_info(stdout, "After loop", &info);
}
else
fprintf(stderr, "Failed to open file %s (%s)\n", argv[i], strerror(errno));
}
dump_info(stdout, "End of main loop", &info);
return 0;
}
le code n'est pas optimale dans la plupart des sens - il attribue loin à o beaucoup de petits morceaux de mémoire. J'ai aussi été paresseux et n'ai pas écrit le code pour libérer la mémoire. Cependant, je ne pense pas que ce serait une bonne idée d'inclure cela dans votre code.
Qu'avez-vous écrit? Quelles structures de données envisagez-vous d'utiliser? Quelle est la variable des formats (les autres fichiers auront-ils des ensembles de colonnes différents, ou les ensembles de colonnes seront-ils tous corrigés)? Que devez-vous faire après la lecture des données? –
Pour répondre à votre dernière question, vous devriez vous attendre à un retour à la ligne après chaque ligne - en supposant que vous utilisiez 'fgets()' pour lire le fichier en lignes. –