#include #include #include #include #include //#define INLINE static inline #define INLINE /**/ /* === CSV-STRING Interface === */ typedef struct { char *str; int cap, pos; } csv_string_t; INLINE void csv_string_init(csv_string_t *csv_string) { static const int INITIAL_CAP = 16; if ( (csv_string->str = malloc(INITIAL_CAP)) != NULL ) { csv_string->cap = INITIAL_CAP; csv_string->pos = 0; } } INLINE void csv_string_reset(csv_string_t *csv_string) { csv_string->pos = 0; } INLINE bool csv_string_empty(csv_string_t *csv_string) { return ( csv_string->pos == 0 ) ? true : false; } INLINE void csv_string_append(csv_string_t *csv_string, int ch) { if ( csv_string->pos == csv_string->cap ) { /* grow if needed */ int cap = (csv_string->cap * 3) / 2; /* *= 1.5 */ char *str = realloc(csv_string->str, cap); if ( str == NULL ) { return; } csv_string->str = str; csv_string->cap = cap; } csv_string->str[csv_string->pos++] = ch; /* append char */ } INLINE void csv_string_free(csv_string_t *csv_string) { free(csv_string->str); } /* === CSV-FIELD Interface === */ typedef struct { int *fields; int cap, pos; } csv_field_t; INLINE void csv_field_init(csv_field_t *csv_field) { static const size_t INITIAL_CAP = 16; if ( (csv_field->fields = malloc(INITIAL_CAP * sizeof(csv_field->fields[0]))) != NULL ) { csv_field->cap = INITIAL_CAP; csv_field->pos = 0; } } INLINE void csv_field_reset(csv_field_t *csv_field) { csv_field->pos = 0; } INLINE void csv_field_append(csv_field_t *csv_field, int idx) { if ( csv_field->pos == csv_field->cap ) { /* grow if needed */ int cap = (csv_field->cap * 3) / 2; /* *= 1.5 */ int *fields = realloc(csv_field->fields, cap * sizeof(csv_field->fields[0])); if ( fields == NULL ) { return; } csv_field->fields = fields; csv_field->cap = cap; } csv_field->fields[csv_field->pos++] = idx; /* append idx */ } INLINE void csv_field_free(csv_field_t *csv_field) { free(csv_field->fields); } /* === CSV Interface === */ typedef struct { char quote_symbol; char sep_symbol; } csv_options_t; static csv_options_t default_csv_options = { .quote_symbol = '"', .sep_symbol = ',' }; typedef struct { csv_options_t *csv_options; csv_string_t csv_string; csv_field_t csv_field; } csv_t; void csv_init_opt(csv_t *csv, csv_options_t *csv_options) { csv->csv_options = ( csv_options != NULL ) ? csv_options : &default_csv_options; csv_string_init(&csv->csv_string); csv_field_init(&csv->csv_field); } void csv_init(csv_t *csv) { csv_init_opt(csv, &default_csv_options); } void csv_free(csv_t *csv) { csv_string_free(&csv->csv_string); csv_field_free(&csv->csv_field); } int csv_nfields(csv_t *csv) { if ( csv->csv_string.pos != 0 ) { return csv->csv_field.pos; } return 0; } const char * csv_field(csv_t *csv, int idx) { return &csv->csv_string.str[csv->csv_field.fields[idx]]; } int csv_read(csv_t *csv, FILE *in) { enum { ZST_START_FIELD, ZST_QUOTED_FIELD, ZST_SIMPLE_FIELD, ZST_END_FIELD, ZST_END_LINE, ZST_END_FILE, }; register const int QUOTE = csv->csv_options->quote_symbol; register const int SEP = csv->csv_options->sep_symbol; csv_string_reset(&csv->csv_string); csv_field_reset(&csv->csv_field); for ( int zst = ZST_START_FIELD; ; ) { int ch; switch ( zst ) { case ZST_START_FIELD: csv_field_append(&csv->csv_field, csv->csv_string.pos); ch = getc(in); if ( ch == EOF ) { zst = ZST_END_FILE; } else if ( ch == '\r' ) { /* Teste auf CR.. */ ch = getc(in); if ( ch != '\n' ) { /* ... LF */ ungetc(ch, in); } zst = ZST_END_LINE; } else if ( ch == '\n' ) { zst = ZST_END_LINE; } else if ( ch == SEP ) { zst = ZST_END_FIELD; } else if ( ch == QUOTE ) { zst = ZST_QUOTED_FIELD; } else { csv_string_append(&csv->csv_string, ch); zst = ZST_SIMPLE_FIELD; } break; case ZST_QUOTED_FIELD: do { ch = getc(in); if ( ch == EOF ) { zst = ZST_END_FILE; } else if ( ch == QUOTE ) { ch = getc(in); if ( ch == EOF ) { zst = ZST_END_FILE; } else if ( ch == QUOTE ) { csv_string_append(&csv->csv_string, QUOTE); } else if ( ch == SEP ) { zst = ZST_END_FIELD; } else if ( ch == '\r' ) { ch = getc(in); if ( ch != '\n' ) { ungetc(ch, in); } zst = ZST_END_LINE; } else if ( ch == '\n' ) { zst = ZST_END_LINE; } else { csv_string_append(&csv->csv_string, QUOTE); ungetc(ch, in); /* zuviel gelesenes Zeichen zurückstellen */ } } else { csv_string_append(&csv->csv_string, ch); } } while ( zst == ZST_QUOTED_FIELD ); break; case ZST_SIMPLE_FIELD: do { ch = getc(in); if ( ch == EOF ) { zst = ZST_END_FILE; } else if ( ch == SEP ) { zst = ZST_END_FIELD; } else if ( ch == '\r' ) { ch = getc(in); if ( ch != '\n' ) { ungetc(ch, in); } zst = ZST_END_LINE; } else if ( ch == '\n' ) { zst = ZST_END_LINE; } else { csv_string_append(&csv->csv_string, ch); } } while ( zst == ZST_SIMPLE_FIELD ); break; case ZST_END_FIELD: csv_string_append(&csv->csv_string, '\0'); zst = ZST_START_FIELD; break; case ZST_END_LINE: csv_string_append(&csv->csv_string, '\0'); return csv->csv_field.pos; case ZST_END_FILE: if ( csv_string_empty(&csv->csv_string) ) { return -1; /* EOF reached */ } /* EOF wurde gelesen, allerdings sind Daten zur Vearbeitung vorhanden! */ /* => aktuelles Feld abschließen... */ csv_string_append(&csv->csv_string, '\0'); /* ... und die Anzahl der Felder zurückliefern! */ return csv->csv_field.pos; } } } int main(void) { csv_t csv[1]; int n, line = 0; #if 0 char data[] = "\"\"aaa\",\"b\"\"bb\",\"ccc\"\n" "zzz,,yyy,xxx\n" ",\n" ""; FILE *in = fmemopen(data, strlen(data), "rb"); if ( in == NULL ) { printf("no data...\n"); return 0; } csv_init(csv); while ( (n = csv_read(csv, in)) != -1 ) { printf("%4d: ", ++line); for ( int i = 0; i < n; ++i ) { printf("'%s' ", csv_field(csv, i)); } putchar('\n'); if ( line == 100 ) break; } csv_free(csv); #endif #if 0 csv_init(csv); while ( (n = csv_read(csv, stdin)) != -1 ) { printf("%4d: ", ++line); for ( int i = 0; i < n; ++i ) { printf("'%s' ", csv_field(csv, i)); } putchar('\n'); if ( line == 100 ) break; } csv_free(csv); #endif #if 1 clock_t start = clock(); csv_init(csv); while ( (n = csv_read(csv, stdin)) != -1 ) { ++line; } csv_free(csv); clock_t end = clock(); printf("%d lines processed, duration: %.3lf sec\n", line, ((double)(end - start)) / CLOCKS_PER_SEC); return EXIT_SUCCESS; #endif }