Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------- MANEJO DE ARCHIVOS EN C. Archivos binarios. En los archivos de texto teníamos registros formados por una cierta cantidad de caracteres alfanuméricos y finalizados en el par CR-LF (o sea Carriadge Return y Line Feed), brindando el lenguaje un conjunto de funciones para el manejo cómodo de dichos archivos. En los archivos binarios el concepto de dato se pierde, pues ahora sólo tenemos un flujo de bytes hacia y desde un dispositivo de almacenamiento. Ello no quiere decir que no podamos manejar perfectamente archivos de texto, sólo que con un poco más de trabajo. Pero se puede. Para abrir un archivo en forma binaria y para lectura, haremos lo siguiente. if((Archi=fopen(NombArchi,"rb")==NULL) { cprintf("No pudo abrir el archivo fuente"); getch( ); return; } y para abrirlo en modo escritura: if((Archi=fopen(NombArchi,"wb")==NULL) { cprintf("No pudo abrir el archivo fuente"); getch( ); return; } Siempre es conveniente chequear si la operación fue exitosa. También es válido aquí todo lo dicho en el modo texto en cuanto a la actualización de archivos, salvo que ahora va la letra 'b' y no la 't'. Por ejemplo: ab rb+ wb+ etc. Existen dos funciones claves para el manejo de archivos binarios: int fread(void *Donde_Guarda, int Tam_Bloque, int NroDeBloques, FILE *stream) int fwrite(void *Desde_Saca, int Tam_Bloque, int NroDeBloques, FILE *stream) Si estamos leyendo - fread( ) - el primer parámetro indica la dirección a partir de la cual se almacenarán los bytes extraídos del archivo. En cambio si estamos escribiendo -fwrite( ) indicará la dirección desde dónde se extraerán los datos a grabar en el archivo. CLASE DE TEORÍA Nro 6 Página 1 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------El elemento de programa que recibe o emite datos puede ser desde una variable simple hasta arreglos y estructuras complejas de datos. Estas dos instrucciones reciben o emiten paquetes de bytes de la siguiente manera: tamaño de cada bloque de una cantidad de bloques Lo más cómodo es especificar que el tamaño de bloque sea siempre igual a 1, y el número de bloques la cantidad de bytes que deseamos movilizar. Un ejemplo aclarará bien esta idea: const DIM = ..... typedef struct TDatos { ............. }; TDato Datos[DIM]; . . . for(i=0;i<DIM;i++) fread(&Datos[i],1,sizeof(TDatos),Fuente); O sea vamos extrayendo sizeof(TDatos) bytes - tamaño del registro - y lo vamos almacenando en cada domicilio del vector de estructuras. En este caso particular TODOS los registros lógicos poseen la misma extensión en bytes. Pero no siempre es así. Una gran ventaja de tener todos los registros de igual tamaño, es que podemos acceder aleatoriamente a cualquiera de ellos ya que en el disco se comportan como un arreglo, disponiendo C de instrucciones precisas para accesar a cualquier posición remota. Veremos esto más adelante con un ejemplo. Por el momento veamos cómo trabajar con archivos de texto pero en modo binario. /* --- TALLER DE LENGUAJES I 2014 Leer archivo de texto con fread.cpp Este programa lee en forma binaria un archivo de texto. Para esto es necesario leer byte a byte hasta alcanzar el caracter CR e ir almacenando en cada domicilio Linea[i] el caracter extraído. El alcanzar cada CR debe ocurrir varias cosas: - No incorporar ese caracter a la cadena. - Leer en vacio un caracter mas (el LF) para dejar listo la prox línea. - Agregar el finalizador '\0' a la cadena extraída. Lo mas conveniente es implementar una función de usuario denominada LeerLinea(FILE *, char *). CLASE DE TEORÍA Nro 6 Página 2 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------NOTA: La búsqueda de cadenas a extraer finaliza cuando la cantidad de caracteres leído en un ciclo de lectura es 0 : fread( )==0 Tampoco se considero el caso en que haya TAB intercalados en c/ línea del archivo. Esto es fácil de solucionar. -------------------------------------------------------------------------------------------------------- */ #include <conio.h> #include <stdlib.h> #include <stdio.h> #include <string.h> const CR = 13; const LF = 10; int LeerLinea( FILE *, char * ); // ----------------------------------------------------------------------------------------void main() { FILE *Fuente; char NombFuente[128] = "D:\\DISCO F\\Leng214\\Buscar medida en pulgadas.cpp"; char Linea[128]; int i; clrscr(); if((Fuente=fopen(NombFuente,"rb"))==NULL){ cprintf("NO PUDO ABRIR ARCHIVO FUENTE"); getch(); return; } while(LeerLinea(Fuente,Linea)) cprintf("%s\r\n",Linea); fclose(Fuente); getch(); } // --------------------------------------------------------------------------------------------------------- CLASE DE TEORÍA Nro 6 Página 3 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------int LeerLinea(FILE *Archi,char *Linea) { char Car; int i = 0; while(fread(&Car,1,1,Archi)) { if(Car!=CR) Linea[i++]=Car; else { fread(&Car,1,1,Archi); Linea[i]='\0'; return(1); } } return(0); } // ------------------------------------------------------------------------------------------------------Aquí estamos leyendo línea a línea el programa fuente "Buscar medida en pulgadas.cpp" el cual se trata, obviamente, de un archivo de texto. El problema es: ¿qué longitud tendrá cada registro a extraer?: ¡¡cualquiera!!, no lo sabemos. En un texto cada renglón puede tener cualquier cantidad de caracteres. Entonces tendremos que implementar nuestra propia función de usuario que nos permita leer cada línea del archivo. Debemos aclarar un detalle importante: la función fread( ) retorna la cantidad de bytes leídos en cada proceso de lectura. Esto quiere decir que podríamos utilizar la misma función para detectar cuándo hemos llegado al final del archivo: en ese momento no se pueden extraer más caracteres y retorna 0. Ello se refleja en: while(fread(&Car, 1, 1, Archi)) o sea mientras fread( ) sea distinto de 0. ¿Cómo sabremos cuándo hemos llegado al final de cada línea?: detectando el par CR-LF while(fread(&Car,1,1,Archi)) { if(Car!=CR) Linea[i++]=Car; else { fread(&Car,1,1,Archi); Linea[i]='\0'; return(1); } } return(0); CLASE DE TEORÍA Nro 6 Página 4 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------Aquí dice lo siguiente: si el caracter actual no es un CR, quiere decir que no hemos llegado al final de la línea y debemos, por lo tanto, almacenarlo en la cadena. Ahora bien, en el caso de haber alcanzado CR, sabemos que a la par existe un LF que no debemos tomar. Leemos dicho caracter en vacío (sin agregarlo a la cadena), colocamos el finalizador '\0' y retornamos (con valor 1) al punto de invocación, expresando que pudo extraer exitosamente el registro actual del archivo de texto. Este proceso se repite hasta alcanzar el último registro. Ahora vamos a proceder al revés: vamos a partir de datos individuales, vamos a armar una cadena de texto y por último vamos a grabarla en un archivo de texto. /* --- TALLER DE LENGUAJES I 2014 La funcion sprintf con archivos binario.cpp Este programa genera un archivo de texto, pero trabajando con archivos binarios. Para ello, se conforma la cadena con sprintf( ) -la cual finaliza la línea con '\' y no con el par CR+LF, el cual normalmente es agregado por fprintf( ). Ello quiere decir que dichos caracteres deberán ser agregados por nosotros a través de la función strcat( ). Una vez completa de este modo, la cadena es transferida al archivo con la function fwrite( ). -------------------------------------------------------------------------------------------------------- */ #include <conio.h> #include <stdlib.h> #include <stdio.h> #include <string.h> const CR = 13; const LF = 10; // -----------------------------------------------------------------------------------------------------------void main() { FILE *Destino; char NombDestino[128] = "D:\\DISCO F\\Leng214\\Archivo para prueba de sprintf.txt"; int Codigo = 3518; char Nombre[30] = "JOSE JULIAN JUAREZ"; float SupCub = 324.58; char Estado[20] = "INMEJORABLE"; char Linea[128]; int i; CLASE DE TEORÍA Nro 6 Página 5 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------clrscr(); if((Destino=fopen(NombDestino,"wb"))==NULL) { cprintf("NO PUDO CREAR ARCHIVO DESTINO"); getch(); return; } sprintf(Linea,"%10d%30s%10.2f%20s",Codigo,Nombre,SupCub,Estado); strcat(Linea,"\r\n"); for(i=0;i<10;i++) { fwrite(Linea,1,strlen(Linea),Destino); cprintf("%s%c",Linea,'!'); } fclose(Destino); getch(); } // --------------------------------------------------------------------------------------------------------------- Hemos grabado 10 líneas iguales en el archivo. La función sprintf( ) a diferencia de nuestra conocida printf( ) o cprintf( ) que mostraban información formateada por pantalla, ésta lo hace hacia una cadena de caracteres. Su sintaxis es: sprintf ( char *CadDestino, "... cadena de formatos ...", Variables ); La función sscanf( ). Si bien no es una función de archivos, está relacionado con ellos cuando el mismo es leído línea a línea y se deben extraer datos (o campos) específicos de ellas. Su sintaxis es la siguiente: sscanf(char *Cad, "... cadena de formatos ...", Direcc de variables ); Es similar la función fscanf( ), pero con la diferencia que en lugar de leer con formatos una línea de archivo, lo hace desde una cadena de caracteres. Sin embargo existe una diferencia importante: funciona mal cuando la cadena está formada por un número dado de valores (por ejemplo enteros) y los mismos deben ser extraídos con un lazo for. Por ejemplo la siguiente cadena: 128 314 CLASE DE TEORÍA Nro 6 258 614 879 588 Página 6 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------Es obvio que la solución trivial es la siguiente: sscanf(Cad,"d%d%d%d%d%d%d",&V[0],&V[1],......&V[5]); y funciona muy bien. Pero....¿qué pasaría si tuviésemos 1024 o más valores en la misma cadena?¿escribiríamos 1024 veces %d y 1024 veces &V[...] ?. No es práctico: lo ideal sería leer la cadena con un lazo for. Pero ahí es donde se presentan los problemas. Veamos por qué: La función fscanf( ) tienen una particularidad: un puntero de archivo que va incrementándose a medida que leemos el archivo. Ello haría que en un lazo de lectura: for(i=0;i<6;i++) fscanf(Archi,"%d",&V[ i ]); se extraigan sin problemas. Sin embargo ello mismo mismo aplicado a sscanf( ) no funciona: for(i=0;i<6;i++) sscanf(Cad,"%d",&V[ i ]); LEE SIEMPRE sólo el primer valor almacenado en Cad porque al completar cada ciclo del lazo for, arranca siempre desde el comienzo de la misma. Para solucionar es pequeño inconveniente hemos desarrollado el siguiente programa: /* --- TALLER DE LENGUAJES I sscanf para un lazo for.cpp 2014 Este programa elimina la limitación de sscanf( ) cuando debe extraer de una misma línea de texto un conjunto de variables de una a la vez mediante un lazo for (puesto que siempre vuelve a extraer desde el inicio). La llamaremos sscanf_( char *). En esta función, i hace de puntero interno a la cadena para quen en la próxima invocación pueda rastrear el valor numérico que continua al anterior. Algo similar a lo que hace la función fscanf( ) con el puntero de archivo. -------------------------------------------------------------------------------------------------- */ #include <conio.h> #include <stdlib.h> #include <string.h> const DIM = 6; CLASE DE TEORÍA Nro 6 Página 7 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------int sscanf_ ( char * ); // ---------------------------------------------------------------------------------------------------------void main() { char Linea[128] = "128 314 258 614 879 588"; int V[DIM]; int i; clrscr(); for(i=0;i<DIM;i++) V[i]=sscanf_(Linea); for(i=0;i<DIM;i++) cprintf("%4d",V[i]); } // ---------------------------------------------------------------------------------------------------------int sscanf_ (char *Cad) { static int i = 0; int Aux; while(Cad[i]==' ') i++; Aux=atoi(&Cad[i]); while((Cad[i]!=' ') && (Cad[i]!='\0')) i++; return(Aux); } // ----------------------------------------------------------------------------------------------------------La función sscanf_ sustituirá a la predefinida sscanf( ) del lenguaje, e implica lo siguiente: generamos con "i" una suerte de puntero interno que se vaya actualizando con cada lectura consecutiva. Para ello la hemos declarado con el prefijo static que tiene la siguiente particularidad: Se inicializa UNA SOLA VEZ al ingresar por primera vez a la función. Se queda CON EL ULTIMO valor que tomó dentro de la función. Esto produciría que cada vez que se ingresa nuevamente a la función "i" mantiene el valor que había obtenido durante la invocación anterior. Lo que hace esta función es "saltear" blancos iniciales o intermedios entre dos cadenas numéricas y tomar la dirección a partir de la cual se hallan caracteres numéricos. Así por ejemplo: CLASE DE TEORÍA Nro 6 Página 8 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------while(Cad[ i ] == ' ') i++; ---> saltea blancos previos al primer caracter numérico. Aux = atoi ( &Cad[i] ); ---> convierte a valor numérico la cadena numérica. while((Cad[ i ]!=' ') && (Cad[ i ] !='\0')) i++; ---> mueve "i" hasta el próximo blanco. y como "i" era static, lo encuentra con este valor actualizado al invocar nuevamente. Ahora vamos a hacer algunas modificaciones al programa anterior para que pueda leer un archivo de texto (obviamente en forma binaria) y almacenar valores, no en un arreglo, sino en una matriz de enteros de DIM1 x DIM2 domicilios. He aquí el código fuente; /* --- TALLER DE LENGUAJES I 2014 Leer matriz desde archivo de texto pero en forma binaria.cpp Este codigo va leyendo línea a línea (en forma binaria) un archivo de texto y toma en cuenta que si se topa con un TAB, este sea convertido en 4 espacios en blanco. También se ha agregado una pequeña modificación en sscanf_( ) que reinicializa el "puntero" de línea" i", cuando se ha llegado al final de cada cadena. ------------------------------------------------------------------------------------------------------------ */ #include <conio.h> #include <stdlib.h> #include <string.h> #include <stdio.h> const DIM1 = 10; const DIM2 = 8; typedef int TMat[DIM1][DIM2]; int LeerLinea ( FILE *, char * ); int sscanf_ ( char * ); // -------------------------------------------------------------------void main() { FILE *Fuente; char NombFuente[128] = "D:\\DISCO F\\Leng214\\Matriz como texto.txt"; char Linea[128]; TMat M; int i, j; clrscr(); CLASE DE TEORÍA Nro 6 Página 9 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------if((Fuente=fopen(NombFuente,"rb"))==NULL) { cprintf("NO PUDO ABRIR ARCHIVO FUENTE"); getch(); return; } for(i=0;i<DIM1;i++) { LeerLinea(Fuente,Linea); for(j=0;j<DIM2;j++) M[i][j]=sscanf_(Linea); } for(i=0;i<DIM1;i++) { for(j=0;j<DIM2;j++) cprintf("%4d",M[i][j]); cprintf("\r\n"); } } // ---------------------------------------------------------------------------------------------------------------int sscanf_(char *Cad) { static int i = 0; int Aux; while(Cad[i]==' ') i++; Aux=atoi(&Cad[i]); while((Cad[i]!=' ') && (Cad[i]!='\0')) i++; if(Cad[i]=='\0') i=0; return(Aux); } // --------------------------------------------------------------------------------------------------------------int LeerLinea(FILE *Archi,char *Linea) { static const CR = 13; static const TAB = 9; char Car; int i = 0; int j; CLASE DE TEORÍA Nro 6 Página 10 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------while(fread(&Car,1,1,Archi)!=0) { switch(Car) { case TAB : for(j=0;j<4;j++) Linea[i++]=' '; break; case CR : fread(&Car,1,1,Archi); Linea[i]='\0'; return(1); default : Linea[i++]=Car; } } return(0); } // -----------------------------------------------------------------------------------------------------------------La función LeerLinea( ) ahora toma en cuenta que los blancos del archivo de texto puedan contener tabuladores. En ese caso reemplaza al mismo por 4 espacios en blanco. El resto continúa igual: si se topó con un CR "i" se queda allí y en ese lugar se almacena el finalizador '\0'. Se lee en vació el LF que sigue al CR y se retorna la cadena. for(i=0;i<DIM1;i++) { LeerLinea(Fuente,Linea); for(j=0;j<DIM2;j++) M[ i ][ j ] = sscanf_(Linea); } Para cada valor de "i" se lee una línea de archivo, la cual contiene todas las columnas necesarias manejadas por "j". El archivo de texto utilizado fue el siguiente: 877 982 691 113 807 468 766 365 514 229 446 938 911 672 224 594 383 783 267 984 568 609 878 911 101 556 415 287 996 961 CLASE DE TEORÍA Nro 6 973 466 802 637 519 365 986 848 106 160 851 130 991 496 697 742 117 649 416 946 120 923 853 523 603 679 533 438 685 429 755 695 576 944 222 994 440 800 313 283 976 320 684 656 577 138 189 506 162 132 Página 11 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------En archivos binarios existen dos funciones adicionales que pueden ser muy útiles: putw( ) y getw( ) para escribir y leer, respectivamente, enteros en forma binaria y no de texto. Debemos recordar que una magnitud entera se almacena (en un formato binario) en una cierta cantidad de bytes. Por ejemplo en algunos compiladores antiguos como las versiones de BorlandC para DOS, los enteros se almacenaban en 2 bytes y su rango iba de -32768 hasta +32767 lo cual equivalía a decir que podíamos disponer de 65536 valores diferentes: la mitad con signo negativo y la otra con signo positivo. En otras versiones de compiladores como el BCC32, los enteros normales poseen 4 bytes y su rango obviamente es muchísimo mayor. De eso se trata esta dos funciones, de trabajar en ese formato. /* --- TALLER DE LENGUAJES I 2014 Escribir una lista de enteros con putw.cpp Este programa genera una lista de enteros en un archivo utilizando la función: int putw ( int w, FILE *stream ); Esta función no espera ni genera ninguna alineación especial en el archivo. Exito --> retorna el entero w. Fallo --> retorna EOF Posteriormente se realiza una lectura del archivo numérico generado con putw, con la función complementaria de esta: int getw( FILE *stream); Para detectar el final de archivo convienen utilizar la función feof(FILE *stream) puesto que EOF tambien puede ser un número válido. ------------------------------------------------------------------------------------------------------------- */ #include <conio.h> #include <stdlib.h> #include <stdio.h> CLASE DE TEORÍA Nro 6 Página 12 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------void main( ) { const TOT_NUM = 128; FILE FILE char char int int *Destino; *Fuente; NombDestino[128] = "D:\\DISCO F\\Leng214\\Lista con putw.dat"; NombFuente [128] = "D:\\DISCO F\\Leng214\\Lista con putw.dat"; NumGen; i; clrscr(); randomize(); if((Destino=fopen(NombDestino,"wb"))==NULL) { cprintf("NO PUDO GENERAR ARCHIVO DESTINO"); getch( ); return; } for(i=0;i<TOT_NUM;i++) { NumGen=100+random(900); putw(100+random(900),Destino); } fclose(Destino); // ---------------------------------------------------------------------------------------------------------------if((Fuente=fopen(NombFuente,"rb"))==NULL) { cprintf("NO PUDO ABRIR EL ARCHIVO FUENTE"); getch(); return; } while(!feof(Fuente)) { NumGen = getw(Fuente); cprintf("%4d",NumGen); } fclose(Fuente); getch( ); } // --------------------------------------------------------------------------------------------------------------------- Aquí simplemente hemos creado y rescatado una lista o especie de vector de valores numéricos. Ahora vamos a darle un formato de matriz. CLASE DE TEORÍA Nro 6 Página 13 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------/* --- TALLER DE LENGUAJES I 2014 Cargar matriz con tabla creada con putw.cpp Este programa genera una lista de enteros en formato binario utilizando la función putw( ), con los cuales se piensa posteriormente alimentar una matriz 2D de 12 x 10. Esto quiere decir que la lista deberá contener 12x10=120 elementos enteros. Una vez creada la lista, se cierra el archivo y se abre el mismo, pero en modo lectura. La carga de la matriz se realiza leyendo cada elemento con getw( ) y asignándolo a cada elemento M[ i ][ j ]. Finalmente se muestra por pantalla. ------------------------------------------------------------------------------------------------------------ */ #include <conio.h> #include <stdio.h> #include <stdlib.h> const DIM1 = 12; const DIM2 = 10; typedef int TMat[DIM1][DIM2]; // -----------------------------------------------------------------------------------------------------------------void main() { FILE FILE char char TMat int int *Destino; *Fuente; NombDestino[128] = "D:\\DISCO F\\Leng214\\Lista_putw.dat"; NombFuente [128] = "D:\\DISCO F\\Leng214\\Lista_putw.dat"; M; NumGen; i, j; clrscr(); if((Destino=fopen(NombDestino,"wb"))==NULL) { cprintf("NO PUDO GENERAR ARCHIVO DESTINO"); getch( ); return; } for(i=0;i<DIM1*DIM2;i++) { NumGen=100+random(900); putw(NumGen,Destino); } fclose(Destino); // --------------------------------------------------------------CLASE DE TEORÍA Nro 6 Página 14 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------if((Fuente=fopen(NombFuente,"rb"))==NULL) { cprintf("NO PUDO ABRIR ARCHIVO FUENTE"); getch(); return; } for(i=0;i<DIM1;i++) { for(j=0;j<DIM2;j++) { M[ i ][ j ] = getw(Fuente); cprintf("%4d",M[ i ][ j ]); } cprintf("\r\n"); } fclose(Fuente); getch(); } En realidad tendríamos un archivo secuencial donde todos los valores numéricos se hallan uno a la par del otro. La lectura también es secuencial, pero no la asignación de estos valores puesto que los lazos for se encargan de distribuir correctamente las magnitudes leídas. Ahora un problema un poco más interesante: convertir un archivo de texto con formato en un archivo binario con registros de igual longitud. /* --- TALLER DE LENGUAJES I 2014 Convertir de archivo de texto a binario.cpp Este programa lee un archivo de texto con formato utilizando la función fscanf( ) y lo convierte en un archivo binario mediante la función fwrite( ). Como elemento intermediario emplea una estructura (que será el registro lógico) y la irá almacenando para cada registro leído. Es decir, ahora tendremos un nuevo registro lógico (siempre de la misma medida), que será la estructura. ------------------------------------------------------------------------------------------------------------- */ #include <conio.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <alloc.h> CLASE DE TEORÍA Nro 6 Página 15 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------typedef struct { int char float char Codigo; Domicilio[30]; SupCub; Estado[16]; } TDatos; // -----------------------------------------------------------------void main() { TDatos *Datos; FILE *Fuente; FILE *Destino; char NombFuente [128] = "D:\\DISCO F\\Leng214\\Lista de propiedades.txt"; char NombDestino[128] = "D:\\DISCO F\\Leng214\\Lista de propiedades.bin"; char Blancos1[16]; char Blancos2[13]; clrscr(); if((Datos=(TDatos *)malloc(sizeof(TDatos)))==NULL) { cprintf("NO SE PUDO RESERVAR MEMORIA DINAMICA"); getch(); return; } if((Fuente=fopen(NombFuente,"rt"))==NULL) { cprintf("NO PUDO ABRIR ARCHIVO FUENTE"); getch(); return; } if((Destino=fopen(NombDestino,"wb"))==NULL) { cprintf("NO PUDO CREAR ARCHIVO DESTINO"); fclose(Fuente); getch(); return; } while(fscanf(Fuente,"%d%[^\A-Z-a-z]%30c%f%[^\A-Z-a-z]%s\n", &Datos->Codigo, Blancos1, Datos->Domicilio, &Datos->SupCub, Blancos2, Datos->Estado )!=EOF) { cprintf("Codigo : %d\r\n", Datos->Codigo); cprintf("Domicilio : %s\r\n", Datos->Domicilio); cprintf("SupCubierta : %6.2f\r\n", Datos->SupCub); cprintf("Estado : %s\r\n", Datos->Estado); CLASE DE TEORÍA Nro 6 Página 16 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------fwrite(Datos,1,sizeof(TDatos),Destino); cprintf("-------------------------------------\r\n"); // if(getch()==27) break; } fclose(Fuente); fclose(Destino); getch(); } Este programa trabaja sobre un archivo ya conocido por nosotros: 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 Av. Alem 2145 258.50 San Lorenzo 3520 185.00 Crisostomo Alvarez 1948 325.50 Lamadrid 4200 225.00 Amador Lucero 75 450.00 Pje Lavalle 2447 250.00 Av. Ernesto Padilla 568 315.00 Italia 2200 272.00 Pje Virrey Vertiz 1235 285.00 Asuncion 1240 180.00 Bolivia 3518 125.00 Lamadrid 2560 100.00 Constitucion 450 480.00 Baltazar Aguirre 385 295.00 Rondeau 628 150.00 Alsina 2320 280.00 Pueyrredon 520 110.00 Colombia 3485 158.00 Guatemala 618 260.00 Pje Zantillan 3125 290.00 Mejico 350 168.00 Chacabuco 325 200.00 Ayacucho 156 350.00 Bolivar 963 215.00 Jujuy 420 380.00 Las Piedras 932 60.00 Lincoln 530 90.00 Gorriti 180 240.00 Agustin Masa 340 218.00 Viamonte 968 114.00 CLASE DE TEORÍA Nro 6 Habitable Reparaciones Excelente Habitable Excelente Modificaciones Excelente Regular Excelente Excelente Reparaciones Habitable Excelente Reparaciones Habitable Excelente Habitable Reparaciones Regular Excelente Regular Reparaciones Regular Regular Excelente Excelente Reparaciones Regular Excelente Reparaciones Página 17 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------El tramo de código: while(fscanf(Fuente,"%d%[^\A-Z-a-z]%30c%f%[^\A-Z-a-z]%s\n", &Datos->Codigo, Blancos1, Datos->Domicilio, &Datos->SupCub, Blancos2, Datos->Estado )!=EOF) { utiliza los trucos ya explicados en la clase pasada para poder leer correctamente archivos de texto con formatos y extrae, o asigna, a variables individuales del tipo adecuado. Estas variables son, obviamente, cada uno de los miembros de la estructura que constituirá cada registro lógico del archivo binario (todos de la misma longitud). La instrucción: fwrite(Datos,1,sizeof(TDatos),Destino); es la encargada de guardar cada registro en el archivo binario. Aquí se ve mucho mejor lo que dijimos en un comienzo: tomar como tamaño de bloques 1 byte y guardar la estructura completa: sizeof(TDatos). Como segunda parte de este programa, mostraremos otro en el cual, ya grabado el archivo binario, procedemos a leerlo aleatoriamente, o sea saltando a registros remotos: /* --- TALLER DE LENGUAJES I 2014 Leer aleatoriamente un archivo (binario Lista de propiedades_bin).cpp Este programa lee de manera aleatoria 4 registros de un archivo binario. Para ello utiliza la función fseek( ) debiendo tener muy en cuenta que los desplazamientos son en CANTIDAD DE BYTES y no solamente en cantidad de registros. Note la sintaxis: fseek(Fuente,NroReg[ i ]*sizeof(TDatos),SEEK_SET) donde el punto de referencia elegido es desde el origen: SEEK_SET --> a partir del origen del archivo. SEEK_CUR --> a partir de la posición actual del puntero. SEEK_END --> a partir del final del archivo. ------------------------------------------------------------------------------------------------------------- */ CLASE DE TEORÍA Nro 6 Página 18 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------#include <conio.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <alloc.h> typedef struct TDatos { int Codigo; char Domicilio[30]; float SupCub; char Estado[16]; } TDatos; // --------------------------------------------------------------------------------------------------------------------void main( ) { TDatos *Datos; FILE *Fuente; char NombFuente[128] = "D:\\DISCO F\\Leng214\\Lista de propiedades.bin"; int NroReg[4] = { 3,10,28,0 }; // ... registros a leer aleatoriamente. int i; clrscr( ); if((Datos=(TDatos *)malloc(sizeof(TDatos)))==NULL){ cprintf("NO PUDO RESERVAR MEMORIA DINAMICA"); getch(); return; } if((Fuente=fopen(NombFuente,"rb"))==NULL) { cprintf("NO PUDO ABRIR ARCHIVO FUENTE"); getch(); return; } for(i=0;i<4;i++) { fseek(Fuente,NroReg[i]*sizeof(TDatos),SEEK_SET); fread(Datos,1,sizeof(TDatos),Fuente); cprintf("Codigo : %d\r\n", Datos->Codigo); cprintf("Domicilio : %s\r\n", Datos->Domicilio); cprintf("SupCubierta : %6.2f\r\n", Datos->SupCub); cprintf("Estado : %s\r\n", Datos->Estado); cprintf("---------------------------------------------------------\r\n"); // if(getch()==27) break; } CLASE DE TEORÍA Nro 6 Página 19 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------fclose(Fuente); getch(); } La instrucción: fseek(Fuente,NroReg[i]*sizeof(TDatos),SEEK_SET); encargada de posicionar el puntero de archivo en una posición dada, realiza saltos en cantidades de bytes. Es por eso que si se desea ir al registro 4, en realidad tendríamos que hacer 4.sizeof(TDatos) de lo contrario sólo aterrizaríamos en el byte nro 4. En cuanto a la lectura de cada registro: fread(Datos,1,sizeof(TDatos),Fuente); debe extraerse el paquete completo equivalente a la estructura: sizeof(TDatos). Otro ejemplo de aplicación de archivos binarios. /* --- TALLER DE LENGUAJES I 2014 Archivo binario de números primos.cpp Este programa genera una tabla de números primos entre 2 y 32000 y los va almacenando en un archivo binario con la función putw( ). --------------------------------------------------------------------------------------------------------- */ #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <math.h> void main() { int Total = 32000; int i,j; int EsPrimo; FILE *Destino; char NombDestino[128] = "D:\\DISCO F\\Leng214\\Tabla de Primos.bin"; clrscr(); CLASE DE TEORÍA Nro 6 Página 20 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------if((Destino=fopen(NombDestino,"wb"))==NULL) { cprintf("NO PUDO CREAR ARCHIVO DESTINO"); getch(); return; } for(i=2;i<Total;i++) { EsPrimo=1; for(j=2;j<=sqrt(i);j++) if(i%j==0) { EsPrimo=0; break; } if(EsPrimo) { cprintf("%20d\r\n",i); fwrite(&i,1,sizeof(int),Destino); } } fclose(Destino); cprintf(" ARCHIVO DE PRIMOS GENERADO EXITOSAMENTE..."); getch(); } Dos modos de determinar la longitud de un archivo. Vamos a tratar binariamente un archivo del tipo que sea y generado por la aplicación que sea, a fin de determinar su tamaño en bytes. Existen dos formas: una un tanto artesanal en la cual se realiza un proceso de lectura con fread( ), que, como sabemos nos devuelve en cada ciclo el número de bytes extraídos, y simplemente llevando una cuenta de estos caracteres. La otra: utilizando funciones más específicas como fseek( ) y ftell( ) que nos ahorra varios pasos. Veamos el programa: /* --- TALLER DE LENGUAJES I Tamanio de un archivo.cpp 2014 Este programa determina la longitud en bytes de un archivo de cualquier tipo (binario o de texto). Lo resuelve de dos maneras distintas: una un tanto artesanal recorriendo el archivo hasta el final y la otra utilizando instrucciones mas especificas. --------------------------------------------------------------------------------------------------------------- */ CLASE DE TEORÍA Nro 6 Página 21 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------- #include <conio.h> #include <stdio.h> int FileSize ( char * ); int FileSize_ ( char * ); // ---------------------------------------------------------------------------------------------------------------void main( ) { char NombArchi[128] = "E:\\Leng214\\Tabla de Primos.txt"; clrscr(); cprintf("Tamanio de %s : %d\r\n",NombArchi,FileSize (NombArchi)); cprintf("Tamanio de %s : %d\r\n",NombArchi,FileSize_(NombArchi)); getch( ); } // -------------------------------------------------------------------------------------------------------------int FileSize(char *NombArchi) { FILE *Aux; char Buff[1024]; int Cuenta = 0; int Leidos; if((Aux=fopen(NombArchi,"rb"))==NULL) { cprintf("NO PUDO ABRIR EL ARCHIVO."); getch(); return(-1); } while((Leidos=fread(Buff,1,sizeof(Buff),Aux))!=0) Cuenta+=Leidos; fclose(Aux); return(Cuenta); } // -------------------------------------------------------------------------------------------------------------int FileSize_(char *NombArchi) { FILE *Archi; int Tamanio; if((Archi=fopen(NombArchi,"rb"))==NULL) { cprintf("NO PUDO ABRIR EL ARCHIVO."); getch(); return(-1); } CLASE DE TEORÍA Nro 6 Página 22 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------fseek(Archi,0,SEEK_END); Tamanio=ftell(Archi); fclose(Archi); return(Tamanio); } En la primer función: while((Leidos=fread(Buff,1,sizeof(Buff),Aux))!=0) Cuenta+=Leidos; los bytes leídos son acumulados en Cuenta, la cual, al final, contendrá un equivalente del total de bytes del archivo. La segunda función de usuario hace lo siguiente: fseek(Archi,0,SEEK_END); Tamanio=ftell(Archi); envía el puntero de archivo al final del mismo mediante seek( ) y luego solicita que indique la posición de dicho puntero: ftell( ). fseek( ) dice: vaya al final del archivo (SEEK_END) y tome un desplazamiento 0 mientras que ftell( ) retorna a cuántos bytes del origen se halla este apuntador. NOTA: Debemos hacer notar un detalle: si hacemos click derecho sobre el archivo evaluado: CLASE DE TEORÍA Nro 6 Página 23 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------- se visualizarán dos tamaños: El tamaño real. El tamaño en disco. Lo que nosotros hemos determinado con nuestro programa es el tamaño real. Como el sistema operativo posee un paquete mínimo de acceso en disco que es el "cluster", cuya extensión en bytes se establece en el momento de particionar y formatear el disco, 4096 en nuestro caso, al guardar un archivo de 5 bytes, en Propiedades leeríamos: Tamaño : 5 Tamaño en disco: 4096 o sea que en archivos pequeños estamos desperdiciando cierta cantidad de bytes. CLASE DE TEORÍA Nro 6 Página 24 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------- Implementando un replicador de archivos. Otra aplicación útil y sencilla de implementar con archivos binarios es un replicador o duplicador de archivos. Simplemente se trata de un proceso sistemático de lectura/escritura hasta finalizar el archivo en cuestión. /* --- TALELR DE LENGUAJES I 2014 - Replicador.cpp Este programa replica (copia) un archivo en la misma o en alguna otra carpeta y lo mismo con el nombre. ------------------------------------------------------------------------------------------------------------------ */ #include<conio.h> #include<stdio.h> #include<stdlib.h> // ------------------------------------------------------------------------------------------------------------------void main() { FILE *Fuente; FILE *Destino; char NombFuente [128] = "E:\\JOE DOLAN\\Crazy woman - Joe Dolan.wav"; char NombDestino[128] = "G:\\Crazy woman - Joe Dolan.wav"; char BuffFuente [32768]; int Leidos; int i; if((Fuente=fopen(NombFuente,"rb"))==NULL) { cprintf("ARCHIVO FUENTE NO ENCONTRADO\r\n"); getch(); return; } if((Destino=fopen(NombDestino,"wb"))==NULL) { cprintf("ARCHIVO DESTINO NO ENCONTRADO\r\n"); getch(); fclose(Fuente); return; } while((Leidos=fread(BuffFuente,1,32768,Fuente))!=0) fwrite(BuffFuente,1,Leidos,Destino); CLASE DE TEORÍA Nro 6 Página 25 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------fclose(Fuente); fclose(Destino); cprintf("ARCHIVO REPLICADO\r\n"); getch(); } Las líneas: while((Leidos=fread(BuffFuente,1,32768,Fuente))!=0) fwrite(BuffFuente,1,Leidos,Destino); realizan una operación simétrica: la misma cantidad de bytes que lee fread( ) es la que copia o replica fwrite( ). Comparador de archivos. /* --- TALLER DE LENGUAJES I 2014 Comparador de archivos 2.cpp Este programa compara dos archivos de texto: "Texto para lectura.txt" y "Texto para lectura con diferencia.txt", practicamente iguaes, salvo una letra que hemos escrito mal a proposito: una c por una s. Primero compara las longitudes: si son diferentes asume que los archivos tambien lo son. Si las longitudes son iguales comienza a leer bloques de tamaño ientico y compara byte a byte. Mientras tanto va llevando una cuenta del numero de caracteres comparados a fin de poder establecer el valor exacto a partir del cual encuentra la primera diferencia. ------------------------------------------------------------------- */ #include<conio.h> #include<stdio.h> #include<stdlib.h> // --------------------------------------------------------------------------------------void main() { FILE *Fuente1; FILE *Fuente2; char NombFuente1[128] = "D:\\DISCO F\\Leng214\\Texto para lectura.txt"; CLASE DE TEORÍA Nro 6 Página 26 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------char rencia.txt"; char char int int int int NombFuente2[128] = "D:\\DISCO F\\Leng214\\Texto para lectura con difeBuff1[128]; Buff2[128]; Tam1,Tam2; Leidos; ComDeDiff = 0; i; if((Fuente1=fopen(NombFuente1,"rb"))==NULL) { cprintf("ARCHIVO FUENTE 1 NO ENCONTRADO\r\n"); getch(); return; } if((Fuente2=fopen(NombFuente2,"rb"))==NULL) { cprintf("ARCHIVO FUENTE 2 NO ENCONTRADO\r\n"); getch(); fclose(Fuente1); return; } fseek(Fuente1,0,SEEK_END); Tam1=ftell(Fuente1); fseek(Fuente2,0,SEEK_END); Tam2=ftell(Fuente2); fseek(Fuente1,0,SEEK_SET); // ... reposiciona al comienzo. fseek(Fuente2,0,SEEK_SET); // ... reposiciona al comienzo. if(Tam1!=Tam2) { cprintf("POSEEN DISTINTOS TAMANIOS"); getch(); fclose(Fuente1); fclose(Fuente2); return; } while((Leidos=fread(Buff1,1,128,Fuente1))!=0) { fread(Buff2,1,128,Fuente2); for(i=0;i<Leidos;i++) { if(Buff1[i]!=Buff2[i]) { ComDeDiff+=i; cprintf("Los archivos son diferentes.\r\n"); cprintf("Las diff se dieron a partir del byte : %d\r\n",ComDeDiff); cprintf("Caracteres diferentes: %c %c\r\n",Buff1[i],Buff2[i]); getch ( ); fclose(Fuente1); fclose(Fuente2); exit(1); } } ComDeDiff+=Leidos; } getch(); } CLASE DE TEORÍA Nro 6 Página 27 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------- /* --- TALLER DE LENGUAJES I 2014 - Inspeccionador de archivos.cpp Este programa lee en forma binaria y en filas de 8 bytes c/u, un archivo de cualquier naturaleza. En este ejemplo se trata de un archivo de texto (el código fuente de un programa escrito con este mismo entorno). Para el ejemplo dado, se observa que cada línea finalizar con el par CR + LF, o sea retorno de carro mas alimentación de línea. IMPORTANTE: es de hacer notar que cuando se graba un archivo de texto mediante fprintf( ) una cadena que finaliza en '\0', el compilador se encarga de convertirlo en el par CR + LF. -------------------------------------------------------------------------------------------------------- */ #include <conio.h> #include <stdio.h> #include <stdlib.h> void InspeccionarArchivo( char * ); // ----------------------------------------------------------------------------------------------------------void main() { char NombFuente[128] = "D:\\DISCO F\\Leng214\\Trapecios.txt"; clrscr( ); InspeccionarArchivo(NombFuente); getch( ); } // ---------------------------------------------------------------------------------------------------------void InspeccionarArchivo(char *NombFile) { const LF = 10; const CR = 13; FILE int char int *Fuente; Leidos; Buff[16]; i; CLASE DE TEORÍA Nro 6 Página 28 de 29 Carrera de Programador Universitario & Licenciatura en Informática TALLER DE LENGUAJES I 2015 - Dictado: Ing. Juan M. Conti FACET de la Universidad Nacional de Tucumán. ----------------------------------------------------------------------------------------------------------------------if((Fuente=fopen(NombFile,"rb"))==NULL) { cprintf("NO PUDO ABRIR ARCHIVO FUENTE\r\n"); getch(); return; } while((Leidos=fread(Buff,1,8,Fuente))!=0) { for(i=0;i<Leidos;i++) { if((Buff[i]==LF) || (Buff[i]==CR)) textcolor(LIGHTRED); else textcolor(WHITE); cprintf("%02X ",Buff[i]); } cprintf("\r\n"); getch(); } fclose(Fuente); } // ---------------------------------------------------------------------------------------------------------------La línea: cprintf("%02X ",Buff[i]); establece que el contenido de Buff[ i ] saldrá por pantalla como hexadecimal con dos dígitos, rellenando con 0 cuando su valor sólo implique un dígito. CLASE DE TEORÍA Nro 6 Página 29 de 29