C: CSV implementation

I have this code from a programming book:

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

char buf[200];          /* input line buffer */
char* field[20];        /* fields */
char* unquote( char* );
        /* csvgetline: read and parse line, return field count */
        /* sample input: "LU",86.25,"11/4/1998","2:19PM",+4.0625,"abc" */
int csvgetline( FILE* fin )
{
        int nfield;
        char *p, *q;
                        /* spacer */
        if( fgets( buf, sizeof( buf ), fin ) == NULL )
                return -1;
        nfield = 0;
        for (q = buf; (p=strtok(q, ",\n\r")) != NULL; q = NULL)
                field[nfield++] = unquote(p);
        return nfield;
}

/* unquote: remove leading and trailing quote */
char* unquote( char *p )
{
        if( p[0] == '"' ) {
                if( p[strlen(p)-1] == '"' )
                        p[strlen(p)-1] = '\0';
                p++;
        }
        return p;
}

extern char* field[];

/* csvtest main: test csvgetline function */
int main( int argc, char* argv[] )
{
        int i, nf;
        FILE* fp;

        if( argc < 2 ) 
                fp = stdin;
        else
                fp = fopen( argv[1], "r" );

        while(( nf=csvgetline( fp )) != -1 )
                for( i=0 ; i<nf ; i++ )
                        printf( "field[%d] = `%s'\n", i, field );

        return 0;
}

How would I test this code on a file on the command line? I believe you use something like a.out? This file name is csvgetline.c
The book also states that there are problems with this implementation, would anyone know by just looking at it?

I'm no C programmer, but I think I know some basics :wink:

First of all, you'll need to compile this code, try

gcc csvgetline.c  #produces executable file a.out

or

gcc -o csvgetline csvgetline.c  #produces executable file csvgetline

#invoke executable to read from standard input

./a.out OR ./csvgetline

Example:

$ ./a.out
"LU",86.25,"11/4/1998","2:19PM",+4.0625,"abc"
field[0] = `LU'
field[1] = `86.25'
field[2] = `11/4/1998'
field[3] = `2:19PM'
field[4] = `+4.0625'
field[5] = `abc'
^C
$

#invoke executable to read from a file

./a.out file.csv OR ./csvgetline file.csv

Example:

$ ./a.out file.csv
field[0] = `LU'
field[1] = `86.25'
field[2] = `11/4/1998'
field[3] = `2:19PM'
field[4] = `+4.0625'
field[5] = `abc'
$ 

Not sure, but I'd say it's

  1. the processing of pre-defined number of fields (?) even if the actual number of fields exceeds that limit and thus producing wrong output
    I don't know why, but during some testing I found out it will handle 24 fields, but fail when there are >25 fields.
  2. the processing of pre-defined line length even if the actual line length exceeds that limit and thus producing wrong output
    which I'd summarize as "no error checking".

Demo for "too many fields":

$ ./a.out test.csv #file with 26 fields; A,B,C,D,...
field[0] = `E'
field[1] = `E'
field[2] = `E'
field[3] = `E'
field[4] = `E'
field[5] = `F'
field[6] = `G'
field[7] = `H'
field[8] = `I'
field[9] = `J'
field[10] = `K'
field[11] = `L'
field[12] = `M'
field[13] = `N'
field[14] = `O'
field[15] = `P'
field[16] = `Q'
field[17] = `R'
field[18] = `S'
field[19] = `T'
field[20] = `U'
field[21] = `V'
field[22] = `W'
field[23] = `X'
field[24] = `Y'
field[25] = `Z'
$

Demo for "too long line":

$ ./a.out file.csv #file with 8 fields (8 x A..Z), line length 216 characters 
field[0] = `ABCDEFGHIJKLMNOPQRSTUVWXYZ'
field[1] = `ABCDEFGHIJKLMNOPQRSTUVWXYZ'
field[2] = `ABCDEFGHIJKLMNOPQRSTUVWXYZ'
field[3] = `ABCDEFGHIJKLMNOPQRSTUVWXYZ'
field[4] = `ABCDEFGHIJKLMNOPQRSTUVWXYZ'
field[5] = `ABCDEFGHIJKLMNOPQRSTUVWXYZ'
field[6] = `ABCDEFGHIJKLMNOPQRSTUVWXYZ'
field[7] = `ABCDEFGHIJ'  # note this "cut" at exactly 200th character
field[0] = `KLMNOPQRSTUVWXYZ'
$
1 Like

It also can't handle escaped things like "this is a string with a \" mark inside it" But that may not be a bug if you don't consider that valid, since it isn't actually causing a crash.

Basically, they left out all the error checking for brevity.

1 Like

Thank you! You have been such a great help to me lately ^^