Leer archivo .csv en C

12 minutos de lectura

avatar de usuario
zess

Tengo un archivo .csv:

lp;imie;nazwisko;ulica;numer;kod;miejscowosc;telefon;email;data_ur
1;Jan;Kowalski;ul. Nowa;1a;11-234;Budry;123-123-456;jan@go.xxx;1980.05.13
2;Jerzy;Nowak;ul. Konopnicka;13a/3;00-900;Lichowice;(55)333-44-55;jer@wu.to;1990.03.23

Y necesito leer esto en C. Tengo algo de código, pero solo para la conexión.

  • Analizador CSV C: sourceforge.net/projects/cccsvparser C CSV Escritor: sourceforge.net/projects/cccsvwriter

    – Algo algo

    23 de agosto de 2014 a las 21:26

  • Por favor edite su pregunta para mostrar el código que tienes hasta ahora. Debe incluir al menos un esquema (pero preferiblemente un ejemplo mínimo reproducible) del código con el que tiene problemas, luego podemos intentar ayudarlo con el problema específico. También deberías leer Cómo preguntar.

    –Toby Speight

    15 de marzo de 2018 a las 14:24

  • rápido con muchos ejemplos: github.com/liquidaty/zsv

    – mwag

    18 de enero a las 7:40

  • Los datos de entrada de muestra están separados por punto y coma. CSV“El término “CSV” también denota varios formatos separados por delimitadores estrechamente relacionados que utilizan otros delimitadores de campo, como punto y coma”.

    -Peter Mortensen

    7 de marzo a las 18:28


avatar de usuario
seje

Con suerte, esto te ayudaría a comenzar.

Míralo en vivo http://ideone.com/l23He (usando entrada estándar)

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

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("input", "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);
    }
}

Producción:

Field 3 would be nazwisko
Field 3 would be Kowalski
Field 3 would be Nowak

  • Ya que strtok no puede manejar nodos en blanco, ¿cómo abordaría una línea de entrada como "A1,B2,C3,,F5,G6" Estoy usando una combinación de strchr y strcpy pero tengo problemas para obtener el valor ‘G6’.

    – ProfesionalAmateur

    17/09/2014 a las 20:09

  • @ProfessionalAmateur Estaría usando C++. Lo siento. Ver respuestas de c++ sobre csv

    – seje

    17/09/2014 a las 21:20


  • Llamémoslos “tokens”, no “nodos”.

    – Carreras de ligereza en órbita

    17/09/2014 a las 21:26

  • Hay una función simple que uso. Compruebe la función zstrtok(). github.com/fnoyanisi/zString

    – fnisi

    21 de mayo de 2015 a las 9:57


  • Esto no puede manejar 11922;28;;1.

    – cokedude

    25 de octubre de 2017 a las 11:28

El siguiente código está en lenguaje c simple y maneja espacios en blanco. Solo asigna memoria una vez, por lo que se necesita un free() para cada línea procesada.

http://ideone.com/mSCgPM

/* Tiny CSV Reader */
/* Copyright (C) 2015, Deligiannidis Konstantinos

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://w...content-available-to-author-only...u.org/licenses/>.  */


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


/* For more that 100 columns or lines (when delimiter = \n), minor modifications are needed. */
int getcols( const char * const line, const char * const delim, char ***out_storage )

{
const char *start_ptr, *end_ptr, *iter;
char **out;
int i;                                          //For "for" loops in the old c style.
int tokens_found = 1, delim_size, line_size;    //Calculate "line_size" indirectly, without strlen() call.
int start_idx[100], end_idx[100];   //Store the indexes of tokens. Example "Power;": loc('P')=1, loc(';')=6
//Change 100 with MAX_TOKENS or use malloc() for more than 100 tokens. Example: "b1;b2;b3;...;b200"

if ( *out_storage != NULL )                 return -4;  //This SHOULD be NULL: Not Already Allocated
if ( !line || !delim )                      return -1;  //NULL pointers Rejected Here
if ( (delim_size = strlen( delim )) == 0 )  return -2;  //Delimiter not provided

start_ptr = line;   //Start visiting input. We will distinguish tokens in a single pass, for good performance.
                    //Then we are allocating one unified memory region & doing one memory copy.
while ( ( end_ptr = strstr( start_ptr, delim ) ) ) {

    start_idx[ tokens_found -1 ] = start_ptr - line;    //Store the Index of current token
    end_idx[ tokens_found - 1 ] = end_ptr - line;       //Store Index of first character that will be replaced with
                                                        //'\0'. Example: "arg1||arg2||end" -> "arg1\0|arg2\0|end"
    tokens_found++;                                     //Accumulate the count of tokens.
    start_ptr = end_ptr + delim_size;                   //Set pointer to the next c-string within the line
}

for ( iter = start_ptr; (*iter!='\0') ; iter++ );

start_idx[ tokens_found -1 ] = start_ptr - line;    //Store the Index of current token: of last token here.
end_idx[ tokens_found -1 ] = iter - line;           //and the last element that will be replaced with \0

line_size = iter - line;    //Saving CPU cycles: Indirectly Count the size of *line without using strlen();

int size_ptr_region = (1 + tokens_found)*sizeof( char* );   //The size to store pointers to c-strings + 1 (*NULL).
out = (char**) malloc( size_ptr_region + ( line_size + 1 ) + 5 );   //Fit everything there...it is all memory.
//It reserves a contiguous space for both (char**) pointers AND string region. 5 Bytes for "Out of Range" tests.
*out_storage = out;     //Update the char** pointer of the caller function.

//"Out of Range" TEST. Verify that the extra reserved characters will not be changed. Assign Some Values.
//char *extra_chars = (char*) out + size_ptr_region + ( line_size + 1 );
//extra_chars[0] = 1; extra_chars[1] = 2; extra_chars[2] = 3; extra_chars[3] = 4; extra_chars[4] = 5;

for ( i = 0; i < tokens_found; i++ )    //Assign adresses first part of the allocated memory pointers that point to
    out[ i ] = (char*) out + size_ptr_region + start_idx[ i ];  //the second part of the memory, reserved for Data.
out[ tokens_found ] = (char*) NULL; //[ ptr1, ptr2, ... , ptrN, (char*) NULL, ... ]: We just added the (char*) NULL.
                                                    //Now assign the Data: c-strings. (\0 terminated strings):
char *str_region = (char*) out + size_ptr_region;   //Region inside allocated memory which contains the String Data.
memcpy( str_region, line, line_size );   //Copy input with delimiter characters: They will be replaced with \0.

//Now we should replace: "arg1||arg2||arg3" with "arg1\0|arg2\0|arg3". Don't worry for characters after '\0'
//They are not used in standard c lbraries.
for( i = 0; i < tokens_found; i++) str_region[ end_idx[ i ] ] = '\0';

//"Out of Range" TEST. Wait until Assigned Values are Printed back.
//for ( int i=0; i < 5; i++ ) printf("c=%x ", extra_chars[i] ); printf("\n");

// *out memory should now contain (example data):
//[ ptr1, ptr2,...,ptrN, (char*) NULL, "token1\0", "token2\0",...,"tokenN\0", 5 bytes for tests ]
//   |__________________________________^           ^              ^             ^
//          |_______________________________________|              |             |
//                   |_____________________________________________|      These 5 Bytes should be intact.

return tokens_found;
}


int main()

{

char in_line[] = "Arg1;;Th;s is not Del;m;ter;;Arg3;;;;Final";
char delim[] = ";;";
char **columns;
int i;

printf("Example1:\n");
columns = NULL; //Should be NULL to indicate that it is not assigned to allocated memory. Otherwise return -4;

int cols_found = getcols( in_line, delim, &columns);
for ( i = 0; i < cols_found; i++ ) printf("Column[ %d ] = %s\n", i, columns[ i ] );  //<- (1st way).
// (2nd way) // for ( i = 0; columns[ i ]; i++) printf("start_idx[ %d ] = %s\n", i, columns[ i ] );

free( columns );    //Release the Single Contiguous Memory Space.
columns = NULL;     //Pointer = NULL to indicate it does not reserve space and that is ready for the next malloc().

printf("\n\nExample2, Nested:\n\n");

char example_file[] = "ID;Day;Month;Year;Telephone;email;Date of registration\n"
        "1;Sunday;january;2009;123-124-456;jitter@go.xyz;2015-05-13\n"
        "2;Monday;March;2011;(+30)333-22-55;buffer@wl.it;2009-05-23";

char **rows;
int j;

rows = NULL; //getcols() requires it to be NULL. (Avoid dangling pointers, leaks e.t.c).

getcols( example_file, "\n", &rows);
for ( i = 0; rows[ i ]; i++) {
    {
        printf("Line[ %d ] = %s\n", i, rows[ i ] );
        char **columnX = NULL;
        getcols( rows[ i ], ";", &columnX);
        for ( j = 0; columnX[ j ]; j++) printf("  Col[ %d ] = %s\n", j, columnX[ j ] );
        free( columnX );
    }
}

free( rows );
rows = NULL;

return 0;
}

Un ejemplo completo que deja los campos como cadenas terminadas en NULL en el búfer de entrada original y brinda acceso a ellos a través de una matriz de punteros char. Se ha confirmado que el procesador CSV funciona con campos encerrados entre “comillas dobles”, ignorando cualquier carácter delimitador dentro de ellos.

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

// adjust BUFFER_SIZE to suit longest line 
#define BUFFER_SIZE 1024 * 1024
#define NUM_FIELDS 10
#define MAXERRS 5
#define RET_OK 0
#define RET_FAIL 1
#define FALSE 0
#define TRUE 1

// char* array will point to fields
char *pFields[NUM_FIELDS];
// field offsets into pFields array:
#define LP          0
#define IMIE        1
#define NAZWISKo    2
#define ULICA       3
#define NUMER       4
#define KOD         5
#define MIEJSCOw    6
#define TELEFON     7
#define EMAIL       8
#define DATA_UR     9

long loadFile(FILE *pFile, long *errcount);
static int  loadValues(char *line, long lineno);
static char delim;

long loadFile(FILE *pFile, long *errcount){

    char sInputBuf [BUFFER_SIZE];
    long lineno = 0L;

    if(pFile == NULL)
        return RET_FAIL;

    while (!feof(pFile)) {

        // load line into static buffer
        if(fgets(sInputBuf, BUFFER_SIZE-1, pFile)==NULL)
            break;

        // skip first line (headers)
        if(++lineno==1)
            continue;

        // jump over empty lines
        if(strlen(sInputBuf)==0)
            continue;
        // set pFields array pointers to null-terminated string fields in sInputBuf
        if(loadValues(sInputBuf,lineno)==RET_FAIL){
           (*errcount)++;
            if(*errcount > MAXERRS)
                break;
        } else {    
            // On return pFields array pointers point to loaded fields ready for load into DB or whatever
            // Fields can be accessed via pFields, e.g.
            printf("lp=%s, imie=%s, data_ur=%s\n", pFields[LP], pFields[IMIE], pFields[DATA_UR]);
        }
    }
    return lineno;
}


static int  loadValues(char *line, long lineno){
    if(line == NULL)
        return RET_FAIL;

    // chop of last char of input if it is a CR or LF (e.g.Windows file loading in Unix env.)
    // can be removed if sure fgets has removed both CR and LF from end of line
    if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1) == '\n')
        *(line + strlen(line)-1) = '\0';
    if(*(line + strlen(line)-1) == '\r' || *(line + strlen(line)-1 )== '\n')
        *(line + strlen(line)-1) = '\0';

    char *cptr = line;
    int fld = 0;
    int inquote = FALSE;
    char ch;

    pFields[fld]=cptr;
    while((ch=*cptr) != '\0' && fld < NUM_FIELDS){
        if(ch == '"') {
            if(! inquote)
                pFields[fld]=cptr+1;
            else {
                *cptr="\0";               // zero out " and jump over it
            }
            inquote = ! inquote;
        } else if(ch == delim && ! inquote){
            *cptr="\0";                   // end of field, null terminate it
            pFields[++fld]=cptr+1;
        }
        cptr++;
    }   
    if(fld > NUM_FIELDS-1){
        fprintf(stderr, "Expected field count (%d) exceeded on line %ld\n", NUM_FIELDS, lineno);
        return RET_FAIL;
    } else if (fld < NUM_FIELDS-1){
        fprintf(stderr, "Expected field count (%d) not reached on line %ld\n", NUM_FIELDS, lineno);
        return RET_FAIL;    
    }
    return RET_OK;
}

int main(int argc, char **argv)
{
   FILE *fp;
   long errcount = 0L;
   long lines = 0L;

   if(argc!=3){
       printf("Usage: %s csvfilepath delimiter\n", basename(argv[0]));
       return (RET_FAIL);
   }   
   if((delim=argv[2][0])=='\0'){
       fprintf(stderr,"delimiter must be specified\n");
       return (RET_FAIL);
   }
   fp = fopen(argv[1] , "r");
   if(fp == NULL) {
      fprintf(stderr,"Error opening file: %d\n",errno);
      return(RET_FAIL);
   }
   lines=loadFile(fp,&errcount);
   fclose(fp);
   printf("Processed %ld lines, encountered %ld error(s)\n", lines, errcount);
   if(errcount>0)
        return(RET_FAIL);
    return(RET_OK); 
}

  • La respuesta aceptada tratará este CSV de 4 elementos como 6 elementos: QA-Q000630115728222,QA-A0926511569122067,"In 1687 John Phillips, Miltons nephew, produced a Don Quixote made English.",2017-03-07T00:00:00.000Z El ejemplo de Gus Gator lo tratará como los 4 elementos adecuados.

    – Adam Howell

    14 dic 2018 a las 18:31

avatar de usuario
WENDYN

Usar fscanf para leer el archivo hasta que encuentre ';' o \nluego simplemente sáltelo con fscanf(f, "%*c").

int main()
{
    char str[128];
    int result;
    FILE* f = fopen("test.txt", "r");

    /*...*/

    do {
        result = fscanf(f, "%127[^;\n]", str);

        if(result == 0)
        {
            result = fscanf(f, "%*c");
        }
        else
        {
            //Put here whatever you want to do with your value.
            printf("%s\n", str);
        }

    } while(result != EOF);

    return 0;
}

Este código es bastante simple, pero efectivo. Analiza archivos separados por comas con paréntesis. Puede modificarlo fácilmente para adaptarlo a sus necesidades.

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


int main(int argc, char *argv[])
{
  // argv[1] path to csv file
  // argv[2] number of lines to skip
  // argv[3] length of longest value (in characters)

  FILE *pfinput;
  unsigned int nSkipLines, currentLine, lenLongestValue;
  char *pTempValHolder;
  int c;
  unsigned int vcpm; // Value character marker
  int QuotationOnOff; // 0 - off, 1 - on

  nSkipLines = atoi(argv[2]);
  lenLongestValue = atoi(argv[3]);

  pTempValHolder = (char*)malloc(lenLongestValue);

  if(pfinput = fopen(argv[1], "r")) {

    rewind(pfinput);

    currentLine = 1;
    vcpm = 0;
    QuotationOnOff = 0;

    // currentLine > nSkipLines condition
    // skips / ignores first argv[2] lines
    while((c = fgetc(pfinput)) != EOF)
    {
       switch(c)
       {
          case ',':
            if(!QuotationOnOff && currentLine > nSkipLines)
            {
              pTempValHolder[vcpm] = '\0';
              printf("%s,", pTempValHolder);
              vcpm = 0;
            }
            break;

          case '\n':
            if(currentLine > nSkipLines)
            {
              pTempValHolder[vcpm] = '\0';
              printf("%s\n", pTempValHolder);
              vcpm = 0;
            }
            currentLine++;
            break;

          case '\"':
            if(currentLine > nSkipLines)
            {
              if(!QuotationOnOff) {
                QuotationOnOff = 1;
                pTempValHolder[vcpm] = c;
                vcpm++;
              } else {
                QuotationOnOff = 0;
                pTempValHolder[vcpm] = c;
                vcpm++;
              }
            }
            break;

          default:
            if(currentLine > nSkipLines)
            {
              pTempValHolder[vcpm] = c;
              vcpm++;
            }
            break;
       }
    }

    fclose(pfinput);
    free(pTempValHolder);
  }

  return 0;
}

  • Le preguntaría al usuario qué quiere decir con ‘entre paréntesis’, pero ya no es miembro de SO. El código es bastante limpio, pero AFAICS es una forma complicada de copiar todo, desde la línea N hasta el final del archivo y la salida.

    –Jonathan Leffler

    5 de marzo de 2018 a las 2:03

  • Le preguntaría al usuario qué quiere decir con ‘entre paréntesis’, pero ya no es miembro de SO. El código es bastante limpio, pero AFAICS es una forma complicada de copiar todo, desde la línea N hasta el final del archivo y la salida.

    –Jonathan Leffler

    5 de marzo de 2018 a las 2:03

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad