[Consulta] Programa para parsear .csv

Iniciado por astinx, 27 Junio 2012, 07:14 AM

0 Miembros y 1 Visitante están viendo este tema.

astinx

Hola, estoy haciendo un programita que parsea unos templates en base a un .csv y genera unos archivos de texto. Por ejemplo:
Si mi .csv es:
"hola#mundo#alegría"
y mi template es:
"[$1] [$2] hoy estoy lleno de [$3]"
el resultado seria:
"hola mundo hoy estoy lleno de alegría"
Mi problema es como encarar el procesamiento de los tokens. Bien yo podría encarar dos soluciones:

1) Hacerlo con un bucle while ((c=fgetc(archivo))!=EOF) y dentro preguntar si me tope con un '[', luego con un '$', luego con un numero y luego con un ']'. Esto claramente es una chanchada.

2) Otra solución seria leer el contenido del archivo de a trozos almacenandolo en un char* y luego usar alguna de las funciones de la librería string.h para encontrar mi token y reemplazarlo. Esta opción es mas prolija, pero si se diera que elegí leer de a 100 caracteres y en el 99vo carácter esta el carácter '[', y luego al principio del próximo string esta "$3] y ese día blablabla....". No me parsearia correctamente el token, ¡se lo saltearía!.

¿Se les ocurre una solución mejor?

Saludos y gracias por detenerse a leer.
La programación hoy en día es una carrera entre los ingenieros de software intentando construir mejores y más eficientes programas a prueba de idiotas y el Universo intentando producir mejores y más grandes idiotas. De momento, el Universo está ganando

durasno

Hola! creo q mejores soluciones a las q das vos no hay(o al menos no se me ocurre en este momento)... Lo que podes hacer es usar strstr para saber cuando hay un "[$" en la cadena y strtok para separar en tokens el .csv. Igual creo que el gran problema es generar la cadena final "hola mundo hoy estoy lleno de alegría", ya q por lo que veo vas a necesitar un buen manejo de punteros


Saludos


PD: strtok y strstr ambas funciones de string.h
Ahorrate una pregunta, lee el man

do-while

¡Buenas!

Tienes otra solucion posible. Leer el tamaño del fichero con los parametros, asignar dinamicamente memoria para almacenar los datos y leer el bloque entero del fichero. Haces lo mismo con el fichero en el que va a haber sustituciones, y ya te olvidas de cadenas que pueden estar o no cortadas. Luego como bien te han dicho, con strchr, strstr y strtok te vas apañando.

¡Saludos!
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

BlackZeroX

#3
Ammm te cree el código ya que no tengo nada que hacer... malditas vacaciones.

crea un archivo llamado "c:\a.txt" y dentro escribe las palabras separadas con espacios...




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

#define PARSETHIS "[$2] [$2] [$3] [$4] [$300] [$030]"

typedef struct acumstrings
{
    char **ptr;
    size_t count;
} acumstrings;

acumstrings acum;

inline const char* getTokenIndex(const char *str, long *Index)
{
    if (!str) return NULL;
    char *ptr = strstr(str, "[$");
    char *end = NULL;

    if (ptr) {
        ptr += strlen("[$");
        (*Index) = strtol(ptr, &end, 10);
    }

    return ptr;
}

inline char* replace(const char *str, const char *find, const char *rep)
{
    char *ret = NULL;
    size_t strOln;
    size_t strFln;
    size_t strRln;
    const char *end[2];
    size_t sizeL;
    size_t sizeR;


    if (!str) return NULL;

    strOln = strlen(str);

    if (!find || (end[0] = strstr(str, find)) == NULL) {
        ret = (char*)malloc(strOln + 1);
        memcpy(ret, str, strOln + 1);
        return ret;
    }

    strRln = strlen(rep);
    strFln = strlen(find);

    end[1] = (char*)((size_t)end[0] + strFln);

    sizeL = (size_t)end[0] - (size_t)str;
    sizeR = ((size_t)str + strOln) - (size_t)end[1];

    ret = (char*)malloc(sizeL + strRln + sizeR + 1);

    memcpy(ret, str, sizeL);
    memcpy((void*)((int)ret + sizeL), rep, strRln);
    memcpy((void*)((int)ret + sizeL + strRln), end[1], sizeR);

    ret[sizeL + strRln + sizeR] = '\0';

    return ret;
}

int main ()
{
    FILE* file;
    size_t ret;
    char* strLast = NULL;
    char* strNew = NULL;
    char* strNewFind = NULL;
    char strToken[20];
    long index;

    file = fopen("C:\\a.txt", "rb+");

    //  Obtenemos cada palabra.
    while (!feof(file))
    {
        acum.ptr = (char**)realloc(acum.ptr, (acum.count + 1) * sizeof(char**));
        acum.ptr[acum.count] = (char*)calloc(256, 1);
        ret = fscanf(file, "%s", acum.ptr[acum.count]);
        puts(acum.ptr[acum.count]);
        ++acum.count;
    }

    fclose(file);

    strNew = (char*)malloc(strlen(PARSETHIS) + 1);
    strcpy(strNew, PARSETHIS);
    strNewFind = strNew;

    //  reemplazamos cada token por cada una de las palabras respectivas.
    while(strNewFind = getTokenIndex(strNewFind, &index))
    {
        sprintf(strToken, "[%$%d]\0", index);
        if (acum.count > index) {
            strLast = strNew;
            strNew = replace(strLast, strToken, acum.ptr[index - 1]);
            free(strLast);
            strNewFind = strNew;
        } else {
            ++strNewFind;
        }
    }

    while(acum.count--)
        free(acum.ptr[acum.count]);
    free(acum.ptr);

    puts(strNew);

    free(strNew);

    getchar();

    return 0;
}



Dulces Lunas!¡.
The Dark Shadow is my passion.

astinx

Buenisimo, muchas gracias Black, tu solución me sirvió bastante para darme una idea.
Saludos!
La programación hoy en día es una carrera entre los ingenieros de software intentando construir mejores y más eficientes programas a prueba de idiotas y el Universo intentando producir mejores y más grandes idiotas. De momento, el Universo está ganando

Foxy Rider

#5
Ejem ... no era más fácil leer línea a línea (no se agarren la *mala* costumbre de leer todo de un saque), pasar todo por strtok() y tomar los 3 valores y encajarlos en un printf como el ejercicio lo pide?

Saludos.

durasno

Hola! una pregunta BlackZeroX (Astaroth) ¿el codigo q hiciste es en C no?? si es asi podrias explicarme cual es la funcion de inline?? que no lo conocia


Saludos
Ahorrate una pregunta, lee el man

astinx

Cita de: vertexSymphony en 28 Junio 2012, 09:14 AM
Ejem ... no era más fácil leer línea a línea (no se agarren la *mala* costumbre de leer todo de un saque), pasar todo por strtok() y tomar los 3 valores y encajarlos en un printf como el ejercicio lo pide?

Saludos.

Gracias por ilustrarme vertex, heló aquí el código.

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

int main()
{
char * tokens[] = {"cosas","hacerlas"};
    char s1[] = "Las [$0] hay que [$1] en el momento que hay que [$1].\n";
    char s2[] = "[$";
    char *ptr;
    ptr = strtok( s1, s2 );    // Primera llamada => Primer token
    if ( (isdigit(ptr[0])) && (ptr[1] == ']') ) {
//Es un token
printf("%s%s", tokens[atoi(ptr)], ptr+2);
} else {
printf("%s",ptr);
    }
    while( (ptr = strtok( NULL, s2 )) != NULL )    // Posteriores llamadas
if ( (isdigit(ptr[0])) && (ptr[1] == ']') ) {
//Es un token
printf("%s%s", tokens[atoi(ptr)], ptr+2);
} else {
printf("%s",ptr);
}
    return 0;
}


Saludos
La programación hoy en día es una carrera entre los ingenieros de software intentando construir mejores y más eficientes programas a prueba de idiotas y el Universo intentando producir mejores y más grandes idiotas. De momento, el Universo está ganando

BlackZeroX

#8
En mi parecer, al ser un forma de ver el formato de manera uniforme y repetitiva (no compuesto/compleja) donde cada palabra (no  consideran frases) esta solo separada por un signo (en mi caso no era # si no mas bien el espacio o salto de linea '\n' o cualquier carácter que tenga el mismo uso) funciona bastante bien fscanf(file, "%s", ...),se cargan todas la palabras en un "Buffer" para no estar re-leyendo a cada instante el archivo y evitar la latencia de acceso al disco duro por ello yo descarte el uso de strtok() y de lectura de lineas las cuales desconozco su longitud.

* Cuando es un formato del este estilo (palabra1 palabra2 palabra3 palabra4 ... palabraN-esima) y no hay formatos complejos fscanf(file, "%s", ...) va perfecto siempre y cuando haya un separador de palabra, de lo contrario es mas recomendable cargar el archivo en memoria y proseguir a identificar cada palabra/signo clave.

por ejemplo ( Yo cargaría en memoria el archivo si estuviera en el disco y aplicar un analizador sintáctico y enviar todo el archivo a estructuras obviamente ya analizado, a su vez también evitar la mayor cantidad de ciclos incluyendo strtok() ya que en este tipo de cuestiones se pueden omitir o tener palabras/signos opcionales y strtok() sacrificaría demasiado el redimiendo de un algoritmo ademas que ignoraria palabras entre el punto inicial y el punto final, es decir funcionaria solo para identificar la existencia en este caso ):



// este es un comentario

cadena1{ // Note que no hay espacio entre la palabra y {
   casena(propiedad1 = "algo") { // Note que la palabra tiene propiedades; Note que hay un espacio antes del {
        item1 = ""
   }
}



* strtok() esta bien para hacer por ejemplo una función que extraiga un trozo de texto o similares pero para analizar formatos() puede que si y puede que no, en mi criterio es dar muchos vueltas (en formatos complejos) en simples como este no hay bronca pero lo mejor es leer el archivo y meterlo en estructuras que representen al archivo original y así evitar la latencia de acceso al disco duro.

NOTA: la caga en memoria seria por bloques mas no todo de golpe.

Dulces Lunas!¡.
The Dark Shadow is my passion.