Skip to content

Propose example code to parse and show CSV file content #23

Description

@chrisinmtown

I think it would really help if the examples/ area has a program that demonstrates how to get and process CSV data. I offer the following, maybe call it csvdump.c. I believe it frees all resources but please double check, my C skills are a little rusty.

// Parse CSV file using libcsv and emit rows on stdout.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "csv.h"
  
// Storage for one row of data and parse state
typedef struct {
    size_t row;
    char **fields;
    size_t alloc;
    size_t cur_field;
    int error;
} csv_reader;

// Parser calls this function when it detects end of a field
void field_cb(void *field, size_t field_len, void *data) {
    csv_reader *cr = (csv_reader*)data;
    if (cr->error)
        return;

    // check for space to store this field
    if (cr->cur_field == cr->alloc) {
        cr->alloc *= 2;
        cr->fields = realloc(cr->fields, sizeof(char *) * cr->alloc);
        if (cr->fields == NULL) {
            fprintf(stderr, "field_cb: failed to reallocate %zu bytes",
                    sizeof(char *) * cr->alloc);
            perror(NULL);
            cr->error = 1;
            return;
        }
    }

    cr->fields[cr->cur_field] = strndup((char*)field, field_len);
    cr->cur_field += 1;
}

// Parser calls this function when it detects end of a row
void row_cb(int delim __attribute__((unused)), void *data) {
    csv_reader *cr = (csv_reader*)data;
    if (cr->error)
        return;
    printf("Row %zu:\n", cr->row);
    for (size_t i = 0; i < cr->cur_field; ++i) {
        printf("\tField %zu: %s\n", i, cr->fields[i]);
        free(cr->fields[i]);
    }
    cr->cur_field = 0;
    cr->row += 1;
}

// Parse data from file at the specified path.
// Return 0 on success, non-zero on failure.
int parse_csv_file(const char *path) {
    int retval = 1;
    struct csv_parser cp;
    int rc = csv_init(&cp, CSV_STRICT);
    if (rc != 0) {
        fputs("failed to initialize CSV parser\n", stderr);
        goto FINAL;
    }
    // Preallocate space for some fields
    csv_reader cr;
    memset((void*)&cr, 0, sizeof(csv_reader));
    cr.alloc = 16;
    cr.fields = malloc(sizeof(char *) * cr.alloc);
    if (cr.fields == NULL) {
        fprintf(stderr, "failed to allocate %zu bytes\n",
                sizeof(char *) * cr.alloc);
        goto FINAL;
    }
    FILE *fp = fopen(path, "rb");
    if (!fp) {
        fprintf(stderr, "failed to open file %s\n", path);
        retval = 1;
        goto FAIL_FP;
    } 
    char buf[4096];
    size_t bytes_read;
    while ((bytes_read = fread(buf, 1, sizeof(buf), fp)) > 0) {
        if (csv_parse(&cp, buf, bytes_read, field_cb, row_cb, &cr) != bytes_read) {
            fprintf(stderr, "Error while parsing file: %s\n",
                    csv_strerror(csv_error(&cp)) );
            goto FAIL_CR;
        }
    }
    rc = csv_fini(&cp, field_cb, row_cb, &cr); 
    if (cr.error || rc != 0) {
        fputs("Parse failed\n", stderr);
        goto FAIL_CR;
    }

    SUCCESS:
        retval = 0;
    FAIL_CR:
        fclose(fp);
    FAIL_FP:
        free(cr.fields);
    FINAL:
        return retval;
}

// Invoke the parser with the first argument as file path
int main(int argc, const char **argv) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s file.csv\n", argv[0]);
        return 1;
    }
    return parse_csv_file(argv[1]);
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions