Search code examples
cfilecsvfwritefread

How to read and write registers from a CSV file (in C)?


I have a .CSV file and I need to read it and write on a binary file.

I've tried to adapt some codes that I saw in some similar questions, but didn't worked.

My file is like:

nroInscricao,nota,data,cidade,nomeEscola
439,607.5,01/01/2004,Maceio,PEDRO II
387,,,Sao Paulo,JOAO KOPKE
332,400.8,03/01/2004,Brasilia,REINALDO RIBEIRO DA SILVA DOU
296,436.4,04/01/2004,,JOSE CANDIDO DE SOUZA

And I tried to read the file with the following code:

const char* getfield(char* line, int num){

    const char* tok;
    for (tok = strtok(line, ","); tok && *tok; tok = strtok(NULL, ",\n")){
        if (!--num)
            return tok;
    }
    return NULL;

}


int main(){

    FILE* stream = fopen("C:\\Users\\10734140\\Downloads\\SCC0503012019trabalho1.csv", "r+");

    char line[1024];
    while (fgets(line, 1024, stream)){
        char* tmp = strdup(line);
        printf("Field 3 would be %s\n", getfield(tmp, 3));
        // NOTE strtok clobbers tmp
        free(tmp);
    }
}

But nothing came out in the execution.


Solution

  • Just write your strtokEvenEmpty derived from strtok then replace

         for (tok = strtok(line, ","); tok && *tok; tok = strtok(NULL, ",\n")){
    

    by

    for (tok = strtokEvenEmpty(line, ","); tok != NULL; tok = strtokEvenEmpty(NULL, ",\n")){
    

    because your test *tok stops you when a field is empty even before the expected field

    For instance :

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char * strtokEvenEmpty(char * s, const char * seps)
    {
      static char * p = NULL;
    
      if (s != NULL)
        p = s;
      else if (p == NULL)
        return NULL;
      else
        s = p;
    
      while (*p) {
        if (strchr(seps, *p)) {
          *p++ = 0;
          return s;
        }
        p += 1;
      }
      return (*s) ? s : NULL;
    }
    
    const char * getfield(char* line, int num){
      const char * tok;
    
      for (tok = strtokEvenEmpty(line, ","); tok; tok = strtokEvenEmpty(NULL, ",\n")){
        if (!--num)
          return tok;
      }
      return NULL;
    }
    
    int main()
    {
      FILE * stream = fopen("SCC0503012019trabalho1.csv", "r");
    
      if (stream != NULL) {
        char line[1024];
        while (fgets(line, 1024, stream)) {
          printf("Field 3 would be '%s'\n", getfield(line, 3));
        }
        fclose(stream);
      }
    }
    

    Compilation and execution :

    pi@raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall -g s.c
    pi@raspberrypi:/tmp $ cat SCC0503012019trabalho1.csv 
    nroInscricao,nota,data,cidade,nomeEscola
    439,607.5,01/01/2004,Maceio,PEDRO II
    387,,,Sao Paulo,JOAO KOPKE
    332,400.8,03/01/2004,Brasilia,REINALDO RIBEIRO DA SILVA DOU
    296,436.4,04/01/2004,,JOSE CANDIDO DE SOUZA
    pi@raspberrypi:/tmp $ ./a.out
    Field 3 would be 'data'
    Field 3 would be '01/01/2004'
    Field 3 would be ''
    Field 3 would be '03/01/2004'
    Field 3 would be '04/01/2004'
    

    And if I get the fourth field rather than the third (printf("Field 4 would be %s\n", getfield(line, 4));) :

    Field 4 would be 'cidade'
    Field 4 would be 'Maceio'
    Field 4 would be 'Sao Paulo'
    Field 4 would be 'Brasilia'
    Field 4 would be ''
    

    In the current case your strdup in main is useless because the fact line is modified by strtok/strtokEvenEmpty is not a problem, nor the fact line so the result of getfield is modified by the next loop.