Foro de elhacker.net

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...

Código
  1.  
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6.  
  7. #define PARSETHIS "[$2] [$2] [$3] [$4] [$300] [$030]"
  8.  
  9. typedef struct acumstrings
  10. {
  11.    char **ptr;
  12.    size_t count;
  13. } acumstrings;
  14.  
  15. acumstrings acum;
  16.  
  17. inline const char* getTokenIndex(const char *str, long *Index)
  18. {
  19.    if (!str) return NULL;
  20.    char *ptr = strstr(str, "[$");
  21.    char *end = NULL;
  22.  
  23.    if (ptr) {
  24.        ptr += strlen("[$");
  25.        (*Index) = strtol(ptr, &end, 10);
  26.    }
  27.  
  28.    return ptr;
  29. }
  30.  
  31. inline char* replace(const char *str, const char *find, const char *rep)
  32. {
  33.    char *ret = NULL;
  34.    size_t strOln;
  35.    size_t strFln;
  36.    size_t strRln;
  37.    const char *end[2];
  38.    size_t sizeL;
  39.    size_t sizeR;
  40.  
  41.  
  42.    if (!str) return NULL;
  43.  
  44.    strOln = strlen(str);
  45.  
  46.    if (!find || (end[0] = strstr(str, find)) == NULL) {
  47.        ret = (char*)malloc(strOln + 1);
  48.        memcpy(ret, str, strOln + 1);
  49.        return ret;
  50.    }
  51.  
  52.    strRln = strlen(rep);
  53.    strFln = strlen(find);
  54.  
  55.    end[1] = (char*)((size_t)end[0] + strFln);
  56.  
  57.    sizeL = (size_t)end[0] - (size_t)str;
  58.    sizeR = ((size_t)str + strOln) - (size_t)end[1];
  59.  
  60.    ret = (char*)malloc(sizeL + strRln + sizeR + 1);
  61.  
  62.    memcpy(ret, str, sizeL);
  63.    memcpy((void*)((int)ret + sizeL), rep, strRln);
  64.    memcpy((void*)((int)ret + sizeL + strRln), end[1], sizeR);
  65.  
  66.    ret[sizeL + strRln + sizeR] = '\0';
  67.  
  68.    return ret;
  69. }
  70.  
  71. int main ()
  72. {
  73.    FILE* file;
  74.    size_t ret;
  75.    char* strLast = NULL;
  76.    char* strNew = NULL;
  77.    char* strNewFind = NULL;
  78.    char strToken[20];
  79.    long index;
  80.  
  81.    file = fopen("C:\\a.txt", "rb+");
  82.  
  83.    //  Obtenemos cada palabra.
  84.    while (!feof(file))
  85.    {
  86.        acum.ptr = (char**)realloc(acum.ptr, (acum.count + 1) * sizeof(char**));
  87.        acum.ptr[acum.count] = (char*)calloc(256, 1);
  88.        ret = fscanf(file, "%s", acum.ptr[acum.count]);
  89.        puts(acum.ptr[acum.count]);
  90.        ++acum.count;
  91.    }
  92.  
  93.    fclose(file);
  94.  
  95.    strNew = (char*)malloc(strlen(PARSETHIS) + 1);
  96.    strcpy(strNew, PARSETHIS);
  97.    strNewFind = strNew;
  98.  
  99.    //  reemplazamos cada token por cada una de las palabras respectivas.
  100.    while(strNewFind = getTokenIndex(strNewFind, &index))
  101.    {
  102.        sprintf(strToken, "[%$%d]\0", index);
  103.        if (acum.count > index) {
  104.            strLast = strNew;
  105.            strNew = replace(strLast, strToken, acum.ptr[index - 1]);
  106.            free(strLast);
  107.            strNewFind = strNew;
  108.        } else {
  109.            ++strNewFind;
  110.        }
  111.    }
  112.  
  113.    while(acum.count--)
  114.        free(acum.ptr[acum.count]);
  115.    free(acum.ptr);
  116.  
  117.    puts(strNew);
  118.  
  119.    free(strNew);
  120.  
  121.    getchar();
  122.  
  123.    return 0;
  124. }
  125.  
  126.  

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
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.

Código
  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <ctype.h>
  4. #include <stdlib.h>
  5.  
  6. int main()
  7. {
  8. char * tokens[] = {"cosas","hacerlas"};
  9.    char s1[] = "Las [$0] hay que [$1] en el momento que hay que [$1].\n";
  10.    char s2[] = "[$";
  11.    char *ptr;
  12.    ptr = strtok( s1, s2 );    // Primera llamada => Primer token
  13.    if ( (isdigit(ptr[0])) && (ptr[1] == ']') ) {
  14. //Es un token
  15. printf("%s%s", tokens[atoi(ptr)], ptr+2);
  16. } else {
  17. printf("%s",ptr);
  18.    }
  19.    while( (ptr = strtok( NULL, s2 )) != NULL )    // Posteriores llamadas
  20. if ( (isdigit(ptr[0])) && (ptr[1] == ']') ) {
  21. //Es un token
  22. printf("%s%s", tokens[atoi(ptr)], ptr+2);
  23. } else {
  24. printf("%s",ptr);
  25. }
  26.    return 0;
  27. }

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 ):

Código:

// 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!¡.