Как читать и записывать регистры из файла CSV (на C)?

#c #file #csv #fwrite #fread

#c #файл #csv #fwrite #fread

Вопрос:

У меня есть файл .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
  

И я попытался прочитать файл со следующим кодом:

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

    const char* tok;
    for (tok = strtok(line, ","); tok amp;amp; *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 %sn", getfield(tmp, 3));
        // NOTE strtok clobbers tmp
        free(tmp);
    }
}
  

Но при выполнении ничего не вышло.

Комментарии:

1. Проблема с strtok заключается в том, что он будет фильтровать все последовательные появления ',' подобных ",,," , где есть пустые поля. Я думаю, что есть нестандартная версия под названием strtok_r , которая обрабатывает их как отдельные находки, но я мог ошибиться в ее названии — есть версия с некоторым именем.

2. Ах, теперь я вспомнил, это strsep() который нестандартный. На этой справочной странице написано » strsep() Функция была введена в качестве замены для strtok(3) , поскольку последняя не может обрабатывать пустые поля».

3. просто определите свой собственный strtok и измените конечный тест вашего на , смотрите мой ответ

4. предупреждение Я только что исправил ошибку в strtokEvenEmpty , она вернула значение NULL, а не последний токен

Ответ №1:

Просто напишите свой strtokEvenEmpty, производный от strtok, затем замените

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

Автор:

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

потому что ваш тест *tok останавливает вас, когда поле пусто даже перед ожидаемым полем

Например :

 #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);
  }
}
  

Компиляция и выполнение :

 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'
  

И если я получу четвертое поле, а не третье ( printf("Field 4 would be %sn", 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 ''
  

В текущем случае ваш strdup в main бесполезен, потому что строка факта, измененная strtok / strtokEvenEmpty, не является проблемой, равно как и строка факта, поэтому результат getfield изменяется следующим циклом.