Test Foro de elhacker.net SMF 2.1

Programación => Programación C/C++ => Mensaje iniciado por: astinx en 27 Junio 2012, 07:14 AM

Título: [Consulta] Programa para parsear .csv
Publicado por: astinx en 27 Junio 2012, 07:14 AM
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.
Título: Re: [Consulta] Programa para parsear .csv
Publicado por: durasno en 27 Junio 2012, 18:58 PM
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
Título: Re: [Consulta] Programa para parsear .csv
Publicado por: do-while en 28 Junio 2012, 00:07 AM
¡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!
Título: Re: [Consulta] Programa para parsear .csv
Publicado por: BlackZeroX en 28 Junio 2012, 01:49 AM
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!¡.
Título: Re: [Consulta] Programa para parsear .csv
Publicado por: astinx en 28 Junio 2012, 07:48 AM
Buenisimo, muchas gracias Black, tu solución me sirvió bastante para darme una idea.
Saludos!
Título: Re: [Consulta] Programa para parsear .csv
Publicado por: Foxy Rider 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() (http://pubs.opengroup.org/onlinepubs/009695399/functions/strtok.html) y tomar los 3 valores y encajarlos en un printf como el ejercicio lo pide?

Saludos.
Título: Re: [Consulta] Programa para parsear .csv
Publicado por: durasno en 29 Junio 2012, 02:43 AM
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
Título: Re: [Consulta] Programa para parsear .csv
Publicado por: astinx en 3 Julio 2012, 10:42 AM
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() (http://pubs.opengroup.org/onlinepubs/009695399/functions/strtok.html) 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
Título: Re: [Consulta] Programa para parsear .csv
Publicado por: BlackZeroX en 3 Julio 2012, 12:11 PM
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!¡.