domingo, 2 de setiembre de 2007

/leyendo (recursivamente) con fscanf




El 29/08 escribía este post sobre mis problemas para entender fscanf. No es que haya sacado mucho mas en limpio, pero a ensayo y error y leyendo algun que otro man, pude entender al menos lo necesario para resolver el problema.
En general la información o es mas bien críptica o demasiado trivial, por eso dejo un ejemplo concreto, otro más.

La solución fue recursiva nomás. La única restricción es que debía ser llamada dentro de otra función a la cual hay que respetarle el cabezal o su firma.

Para recordar, el formato del archivo a leer es de este tipo:
(Ar 3 (C 2 (d 5) (F 6))(juan 4 )(Ale 5 (Ana 3)))
Entre dos paréntesis consecutivos )) puede haber cualquier cosa, la cual debe ser filtrada.

Arbol* leer(char const* ruta)// firma a respetar.
{
FILE *fp;

fp = fopen(ruta,"r");
Arbol* arbol = NULL;

if (fp)
{
arbol = getArbol(fp,arbol);
fclose(fp);
}

return arbol;
}


Arbol* getArbol(FILE *f, Arbol* a)
{

char c;
char nom[largoNombre];
char lin[512];
int nro;

Arbol* arbol = NULL;
Arbol* subArbol = NULL;

if (!feof(f))
{
fscanf(f,"%[^()]",lin); // leo lo que haya ANTES de ( ó )

fscanf(f,"%c",&c); // lee los paréntesis
if (c == '(') // hay un nodo del arbol
{

fscanf(f," %s %d ",nom,&nro);
arbol = arbolCrear(nom,nro);

subArbol = getArbol(f,arbol); //llamo c/hijos
subArbol = getArbol(f,a); //llamo con padre a leer hnos

if ((a != NULL) && (arbol != NULL))
arbolCambiarPadre(arbol,a); //agrega al arbol
}
}
return arbol;
}

Notas:
  • no es necesario pasar por referencia el puntero a arbol porque la raíz no cambia.
  • fscanf(f,"%[^()]",lin) puede sustituirse por fscanf(f,"*[^()]"), el * indica que lo leído no se almacene en ninguna variable. Como desconocía las consecuencias de leer asi, sin almacenar, decidí optar por la primera. El símbolo ^ saltea blancos, tabuladores, nueva línea y espacios.
  • Otra forma de leer lo mismo hubiera sido fscanf(f," ( %s %d ",nom,&nro)los espacios en blanco son para contemplar el caso que el string o entero inmediato se encuentren después de una nueva línea (algo que puede darse), salvo esa restricción, no vi necesario el espacio entre %s y %d.
  • fscanf(f," %s %d ",nom,&nro). El espacio en blanco saltea nueva línea, tabuladores, los espacios que haya, no importa cuantos, basta poner uno saltea los que haya hasta que encuentre una coincidencia que se ajuste al parámetro de lectura.
  • El carácter "(" es la condición de parada de la recursividad, se terminaron de leer los hijos del nodo actual, se pasa a leer los hermanos del mismo.
Dada la recursividad de la estructura, la función escritura también fue recursiva.

void arbolEscribir(Arbol* arbol,FILE* f)
{

char* nombre = new char[largoNombre];
nombre[19] = '\0';
Arbol* hijo;
int cant_hijos,k;

if (arbol != NULL)
{
fputs("(",f);

arbolNombre(arbol,nombre);

fprintf(f,"%s ",nombre);
fprintf(f,"%d",arbolNumero(arbol));

cant_hijos = arbolCantHijos(arbol);

for (k = 0; (k <>
{
hijo = arbolHijo(arbol,k);
arbolEscribir(hijo,f);
}
fputs(")",f);
}
}

// Guarda arbol en formato de s-expresión en el archivo ruta.
void escribir(Arbol* arbol, char const* ruta)
{
FILE *fp;

if (arbol == NULL)
{
printf("El arbol esta vacio, no hay arbol para guardar\n");
return;
}

if( NULL == (fp = fopen(ruta, "w")) )
{
printf( "ERROR: No se pudo crear el fichero, %s\n",ruta);
return;
}

arbolEscribir(arbol,fp);
fclose(fp);
}









No hay comentarios.: