Está en la página 1de 48

Almacenamiento Externo de Datos

Ficheros

Metodologa de la Programacin II

Tema 2
2.1 Definicin de fichero
Un fichero en sentido global puede ser desde un monitor hasta una impresora, pasando por un
archivo en disco.
La idea ms comn del concepto de fichero es un conjunto de posiciones de memoria situadas en
un disco de los dispositivos externos de almacenamiento del sistema, en las cuales podemos
almacenar y recuperar informacin.
El lenguaje C nos proporciona un acceso secuencial y directo a los registros de un fichero, pero
no soporta el acceso indexado a un registro dado.
Los ficheros en C los podemos clasificar, segn la informacin que contengan, en dos grupos:
Tipo FILE:
C define la estructura de datos FILE en el fichero de cabecera "stdio.h" para el manejo de
ficheros. Nosotros siempre usaremos punteros a estas estructuras.
La definicin de sta estructura depende del compilador, pero en general mantienen un campo
con la posicin actual de lectura / escritura, un buffer para mejorar las prestaciones de acceso
al fichero y algunos campos para uso interno.
2.2 Ficheros de texto y ficheros binarios.
Los ficheros de texto se caracterizan por estar compuestos por una serie de caracteres
organizados en lneas terminadas por un carcter de nueva lnea (carcter '\n'). Esto nos hace
pensar en la idea de usar la impresora como si fuese un fichero de texto.
Por otro lado, los ficheros binarios constan de una secuencia de bytes. Podemos decir que
cualquier fichero que no sea de texto, ser binario.
A la hora de trabajar con ficheros, tendremos que especificar antes de usarlos, si sern de texto o
binarios.
Podemos establecer una segunda clasificacin de los ficheros, atendiendo al modo de acceso a su
informacin. De este modo, distinguiremos entre ficheros secuenciales y ficheros directos.
Los ficheros de acceso secuencial se basan en el hecho de que, para acceder a una determinada
posicin de los mismos, hemos de recorrer desde el principio todas las posiciones hasta llegar a la
deseada. Las impresoras son un claro ejemplo de acceso secuencial, as como las cintas
magnticas.
Con el uso de ficheros de acceso directo podemos acceder de forma directa a la posicin que
queramos sin tener que pasar por las posiciones anteriores. El dispositivo de acceso directo por
excelencia es el dico magntico.
90

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


El lenguaje C trata a los ficheros como punteros. En realidad un fichero es un puntero a una
estructura de nombre predefinido FILE, cuyas componentes son las caractersticas de la variable
fichero declarada. Cada fichero deber tener una estructura FILE asociada.
La estructura FILE se encuentra definida en el archivo de cabecera stdio.h, con lo cual es
necesario incluirla en todos los programas que trabajen con ficheros mediante la conocida
directiva #include <stdio.h>
La forma de declarar variables de tipo FILE es la siguiente:
FILE *f, *f1, ...
Como podemos observar se trata de punteros que apuntan a estructuras de tipo FILE. En realidad
lo que ocurre con el tipo FILE es que redefine a una estructura de los siguiente forma:
typedef struct
{
short
level;
unsigned
flags;
char
fd;
unsigned char hold;
short
bsize;
unsigned char *buffer, *curp;
unsigned
istemp;
short
token;
}FILE;
Esta estructura la podrs ver editando el fichero stdio.h. El programador no tendr que definirla
puesto que ya existe.
A continuacin vamos a ver las operaciones que podemos realizar con ficheros secuenciales y
directos.
2.3 Ficheros de acceso secuencial
La primera oeracin despus de declarar un fichero y antes de cualquier otra operacin con l es
abrirlo.
fopen
<variable_fichero> = fopen (<nombre_fichero>, <modo acceso>);
Donde:
variable_fichero es la variable declarada de la forma: FILE *<variable_fichero>
nombre_fichero es el nombre que tendr el fichero en el dispositivo usado. Podr ser variable o
constante.
Metodologa de la Programacin II

91

Tema 2
Modo acceso es el modo de apertura del fichero, mediante el cual le diremos al compilador si se
trata de un fichero de texto o binario, y si vamos a leer o escribir en el fichero.
Los distintos modos de abrir un fichero son los siguientes:

Modo Significado
r

Abre un archivo de texto para solo lectura. Si el archivo no existe, devuelve un


error

Abre el archivo de texto para solo escritura. Si el fichero no existe, lo crea, y si


existe lo machaca.

Abre el archivo de texto para aadir al final. Si el fichero no existe, lo crea.

r+

Abre el archivo de texto para lectura/escritura. Si el fichero no existe, da un


error

w+

Abre el archivo de texto para lectura/escritura. Si el fichero no existe, lo crea, y


si existe lo machaca.

a+

Abre el archivo de texto para aadir al final, con opcin de lectura. Si el fichero
no existe, lo crea.

rb

Abre un archivo binario para solo lectura. Si el archivo no existe, devuelve un


error

wb

Abre el archivo binario para solo escritura. Si el fichero no existe, lo crea, y si


existe lo machaca.

ab

Abre el archivo binario para aadir al final. Si el fichero no existe, lo crea.

rb +

Abre el archivo binario para lectura/escritura. Si el fichero no existe, da un error

wb + Abre el archivo binario para lectura/escritura. Si el fichero no existe, lo crea, y


si existe lo machaca.
ab +

Abre el archivo binario para aadir al final, con opcin de lectura. Si el fichero
no existe, lo crea.

Decir que los ficheros de texto se pueden referenciar tambien como "rt", "wt", "at", "rt+", "wt+" y
"at+", pero para facilitar ms las cosas, cuando no especificamos si queremos abrir un fichero de
texto o binario, por defecto se supone que es de texto, es por ello que los modos "r", "w" y "a" se
refieren a ficheros de texto.
Hemos de tener cuidado con los modos de apertura de los ficheros por que al abrir con cualquier
modo "w", si ya exista el fichero, se borrar la informacin que contuviese. Si no existe, lo
crear. Por el contrario con el modo "a" tambin lo crear si no existe el fichero, pero si ya existe,
aadir al final del mismo los datos que escribamos.
Podemos saber si un fichero ha sido abierto correctamente o no. Debido a que los ficheros son
punteros a estructuras tipo FILE, podemos conocer su valor al hacer que apunten a ellas. La
forma de hacerlo es comparando el puntero con la constante predefinida NULL. De esta forma,

92

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


cuando el puntero valga NULL (no apunta a ninguna variable o posicion de memoria), es porque
ha habido algn problema al abrir el fichero.
Este problema se puede deber a que no existe el fichero que intentamos abrir para lectura, que no
hay espacio suficiente en el disco al tratar de crear el fichero, que la unidad de disco no est
preparada, que el disco est daado fisicamente o que la impresora no est conectada. Veamos en
ejemplo del testeo de la existencia de un fichero de texto.
FILE *f;
f = fopen ("texto.txt", "r");
if (f == NULL)
printf ("Error, el fichero no existe\n");
else
...
O de esta otra manera
FILE *f;
f = fopen ("texto.txt", "r");
if (!f)
printf ("Error, el fichero no existe\n");
else
...
O bien, de forma abreviada
FILE *f;
if ((f = fopen ("texto.txt", "r")) == NULL)
printf ("Error, el ficnero no existe\n");
else
...
#include <stdio.h>
int main()
{
FILE *fichero;
char nombre[10] = "datos.dat";
fichero = fopen( nombre, "w" );
printf( "Fichero: %s -> ", nombre );
if( fichero )
printf( "creado (ABIERTO)\n" );
else
{
printf( "Error (NO ABIERTO)\n" );
return 1;
}
if( !fclose(fichero) )
printf( "Fichero cerrado\n" );
else

Metodologa de la Programacin II

93

Tema 2
{
printf( "Error: fichero NO CERRADO\n" );
return 1;
}
return 0;
}

fclose
<valor> = fclose (<variable_fichero>);
Donde:
valor es el valor que nos dir si ha ocurrido algn error cerrando el fichero. 0 indica que todo ha
ido bien.
variable_fichero es la variable declarada de la forma: FILE *<variable_fichero>
Cuando terminemos de trabajar con un fichero hemos de realizar la operacin de cierre del
fichero. Si no lo hacemos podemos ocasionar la prdida de todos los datos del mismo. Si se
necesitan cerrar varios ficheros a la vez, nos podemos ahorrar varios fclose utilizando la funcion
fcloseall.
#include <stdio.h>
int main()
{
FILE *fichero;
char nombre[10] = "datos.dat";
fichero = fopen( nombre, "w" );
printf( "Fichero: %s -> ", nombre );
if( fichero )
printf( "creado (ABIERTO)\n" );
else
{
printf( "Error (NO ABIERTO)\n" );
return 1;
}
if( !fclose(fichero) )
printf( "Fichero cerrado\n" );
else
{
printf( "Error: fichero NO CERRADO\n" );
return 1;
}
return 0;
}

fcloseall
<numero_ficheros> = fcloseall();
94

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


Donde:
numero_ficheros es una variable entera que indica el nmero de ficheros cerrados, o EOF si ha
ocurrido un error.
Ahora veamos una serie de funciones para escribir y leer datos de un fichero de texto.
#include <stdio.h>
int main(void)
{
int streams_closed;
/* open two streams */
fopen("DUMMY.ONE", "w");
fopen("DUMMY.TWO", "w");
/* close the open streams */
streams_closed = fcloseall();
if (streams_closed == EOF)
/* issue an error message */
perror("Error");
else
/* print result of fcloseall() function */
printf("%d streams were closed.\n", streams_closed);
return 0;
}
fputc
Esta funcin escribe un carcter en un fichero de texto. Devuelve un entero si la escritura es
correcta. De otra forma devuelve el valor EOF que indica que ha habido un error. Su formato es:
fputc (<carcter>, <var_fich>);
Donde:
carcter es el carcter que se desea escribir en el fichero.
var_fich es la variable declarada como FILE.
Ejemplos:
putc ('a', f1);
c = 'G'; putc (c, f1);
Metodologa de la Programacin II

95

Tema 2
Ni que decir tiene que cuando usemos funciones para escribir en ficheros, estos tendrn que haber
sido abiertos en modo escritura o para aadir datos.
#include <stdio.h>
int main()
{
char nombre[10]="datos.dat";
FILE *fichero;
int i;
fichero = fopen( nombre, "a" );
printf( "Fichero: %s -> ", nombre );
if( fichero )
printf( "existe o ha sido creado (ABIERTO)\n" );
else
{
printf( "Error (NO ABIERTO)\n" );
return 1;
}
printf( "Escribimos las 18 primeras letras del abecedario ingles en el
fichero: %s\n\n", nombre );
for( i=0; i<18; i++)
printf( "%c", fputc('a'+i, fichero) );
if( !fclose(fichero) )
printf( "\nFichero cerrado\n" );
else
{
printf( "\nError: fichero NO CERRADO\n" );
return 1;
}
return 0;
}

fgetc
Esta funcin lee un carcter de un fichero de texto abierto en modo lectura. Devuelve un entero si
la lectura es correcta. De otra forma devuelve el valor EOF que indica que hemos llegado al final
del fichero. Su formato es:
<carcter> = fgetc (<var_fich>);
Donde:
carcter es la variable que contendr el carcter ledo del fichero.
var_fich es la variable declarada como FILE.
Ejemplos:
c = fgetc (f1);

96

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


Un ejemplo del uso de estas dos funciones sera leer todos los caracteres de un fichero de texto y
escribirlos en otro fichero de texto y por pantalla a la vez para comprobar la correcta ejecucin:
#include <stdio.h>
main ()
{
FILE *f_in, *f_out;
char c;
clrscr();
if ((f_in = fopen ("prueba.c", "r")) == NULL)
{
printf ("Error de apertura del fichero\n");
exit (1);
}
if ((f_out = fopen ("salida.c", "w")) == NULL)
{
printf ("Error de creacin del fichero\n");
exit (1);
}
do
{
c = fgetc(f_in);
putchar(c)
/* escritura en pantalla */
putc(c, f_out); /* escritura en el fichero */
}
while (c != EOF);
fclose (f_in);
fclose (f_out);
}
Aparece en este ejemplo una nueva funcin, la funcin exit(). Esta funcin provoca que acabe el
programa con lo que la funcin main() devuelve un valor, en este caso el valor 1. Recordamos
que toda funcin devuelve un valor.
fputs
Esta funcin escribe una cadena de caracteres en un fichero de texto. Su formato es:
fputs (<cadena>, <var_fich>);
Donde:
cadena es el cadena que se desea escribir en el fichero.
var_fich es la variable declarada como FILE.
Ejemplos:
Metodologa de la Programacin II

97

Tema 2
fputs ("hola\n", f1);
strcpy (cad, "hola\n"); fputs (cad, f1);
fgets
Esta funcin lee un nmero de caracteres de un fichero almacenndolos en una cadena. Si se
encuentra el carcter de nueva lnea ya no almacenar ms caracteres. Su formato es:
fgets (<cadena>, <num_caracteres>, <var_fich>);
Donde:
cadena es la variable que almacenar la cadena leda del fichero.
num_caracteres es el nmero de caracteres que se desea leer.
var_fich es la variable declarada como FILE.
Ejemplos:
fgets (cad, 5, f1);
fwrite
Esta funcin permite escribir uno o ms datos o bloques de datos binarios en un fichero. Su
formato es el siguiente:
fwrite (<dato>, <num_bytes>, <cont>, <var_fich>);
Donde:
dato es el dato o datos que se van a escribir en el fichero. Hemos de especificar su direccin de
memoria mediante el operador "&", ya que realmente es un puntero que apunta a la zona de
memoria intermedia donde se almacenar temporalmente la informacin antes de pasarla al
fichero.
num_bytes es el nmero de bytes que se van a escribir. Lo ms corriente es usar para este
argumento el operador sizeof sobre el dato a escribir.
contes el nmero de datos, de num_bytes cada uno que se van a escribir.
var_fich es el puntero al fichero binario usado.
#include <stdio.h>
struct mystruct
{
int i;
98

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


char ch;
};
int main(void)
{
FILE *stream;
struct mystruct s;
if ((stream = fopen("TEST.$$$", "wb")) == NULL) /* open file TEST.$$$ */
{
fprintf(stderr, "Cannot open output file.\n");
return 1;
}
s.i = 0;
s.ch = 'A';
fwrite(&s, sizeof(s), 1, stream); /* write struct s to file */
fclose(stream); /* close file */
return 0;
}
fread
Esta funcin permite leer uno o ms datos o bloques de datos de un fichero binario. Su formato
es:
fread (<dato>, <num_bytes>, <cont>, <var_fich>);
Donde:
dato es la variable donde se guardarn los datos ledos del fichero. Hemos de indicar su direccin
por tratarse de un puntero.
num_bytes es el nmero de bytes que se van a leer del fichero. Lo ms corriente es usar para este
argumento el operador sizeof sobre el dato a escribir.
Cont es el nmero de datos, de num_bytes cada uno que se van a leer.
var_fich es el puntero al fichero binario usado.
Vamos a ver un ejemplo que escriba un float en un fichero y despus lo lea y escriba en pantalla:
#include <stdio.h>
main ()
{
FILE *fich;
float f = 4.879;
/* Escritura del float en el fichero */
if ((fich = fopen ("floats.dat", "wb")) == NULL)
Metodologa de la Programacin II

99

Tema 2
{
printf ("Error de creacin del fichero\n");
exit (1);
}
fwrite (&f, sizeof(f), 1, fich);
fclose (fich);
/* Lectura del float del fichero */
if ((fich = fopen ("floats.dat", "rb")) == NULL)
{
printf ("Error de existencia del fichero\n");
exit (1);
}
fread (&f, sizeof(f), 1, fich);
fclose (fich);
printf ("Float = %.3f", f);
}
Vemos que hemos usado el mismo puntero de fichero. Lo podemos hacer siempre que cerremos
el primero antes de abrir el segundo fichero, o se trate del mismo fichero, como es en este caso.
Primero hemos abierto el fichero binario para escritura, con lo cual lo creamos de nuevo, y
despus lo abrimos para lectura para leer el float.
Este es un ejemplo muy sencillo pues en el fichero solo escribimos un float y despus lo leemos.
Pero, qu ocurre cuando escribimos ms de un dato? cmo sabemos hasta donde debemos leer
en el fichero?. La respuesta est en conocer donde est el final del fichero. De esta forma
leeremos hasta encontrar el final del fichero dado. Existe una funcin que nos dir si hemos
llegado a este punto o no: la funcin feof.
#include <stdio.h>
int main()
{
FILE *fichero;
char nombre[11] = "datos5.dat";
unsigned int dinero[10] = { 23, 12, 45, 345, 512, 345, 654, 287, 567, 124
};
unsigned int leer[10], i;
fichero = fopen( nombre, "w+" );
printf( "Fichero: %s -> ", nombre );
if( fichero )
printf( "creado (ABIERTO)\n" );
else
{
printf( "Error (NO ABIERTO)\n" );
return 1;
}
printf( "Escribiendo cantidades:\n\n" );
for( i=0; i<10; i++ )
printf( "%d\t", dinero[i] );

100

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


fwrite( dinero, sizeof(unsigned int), 10, fichero );
printf( "\nLeyendo los datos del fichero \"%s\":\n", nombre );
rewind( fichero );
fread( leer, sizeof(unsigned int), 10, fichero );
for( i=0; i<10; i++ )
printf( "%d\t", leer[i] );
if( !fclose(fichero) )
printf( "\nFichero cerrado\n" );
else
{
printf( "\nError: fichero NO CERRADO\n" );
return 1;
}
return 0;
}

feof
Esta funcin nos devuelve un nmero positivo si hemos llegado al final de un fichero binario. De
otro modo, nos devuelve un cero. Su sintaxis es:
<valor> = feof (<var_fich>);
Donde:
valor es el valor que nos indica si ha llegado a final de fichero.
var_fich es la variable declarada como FILE.
Ejemplos:
while (!feof(f1))
{
fread (....);
...
}
Este bucle significa "mientras no sea final de fichero leer..."
#include <stdio.h>
int main(void)
{
FILE *stream;
/* open a file for reading */
stream = fopen("DUMMY.FIL", "r");
/* read a character from the file */
Metodologa de la Programacin II

101

Tema 2
fgetc(stream);
/* check for EOF */
if (feof(stream))
printf("We have reached end-of-file\n");
/* close the file */
fclose(stream);
return 0;
}
ferror
Determina si una operacin con archivos ha sido errnea o no. Si lo ha sido devuelve un nmero
positivo, y si no ha habido problema, devuelve un 0. Su sintaxis es:
<valor> = ferror (<var_fich>);
Donde:
valor es el valor que nos indica si ha ocurrido un error.
var_fich es la variable declarada como FILE.
Ejemplos:
fwrite (&dato, sizeof(dato), 1, fich);
if (ferror(fich)) printf ("Error de escritura\n");
#include <stdio.h>
int main(void)
{
FILE *stream;
/* open a file for writing */
stream = fopen("DUMMY.FIL", "w");
/* force an error condition by attempting to read */
(void) getc(stream);
if (ferror(stream)) /* test for an error on the stream */
{
/* display an error message */
printf("Error reading from DUMMY.FIL\n");
/* reset the error and EOF indicators */
clearerr(stream);
}
102

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


fclose(stream);
return 0;
}
rewind
Esta funcin restablece el localizador de posicin del archivo al comienzo del mismo. Su sintaxis
es:
rewind (<var_fich>);
Donde:
var_fich es la variable declarada como FILE.
Por ejemplo, si estamos leyendo secuencialmente el fichero fich y vamos por el cuarto dato, al
hacer rewind(fich), volveramos al principio del fichero y podramos volver a leer el primer dato,
como si lo abrisemos de nuevo para lectura.
Ejemplos:
// ejemplo1.cpp: Muestra un fichero dos veces.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fichero;
fichero = fopen("ejemplo1.cpp", "r");
while(!feof(fichero)) fputc(fgetc(fichero), stdout);
rewind(fichero);
while(!feof(fichero)) fputc(fgetc(fichero), stdout);
fclose(fichero);
system("pause");
return 0;
}
getw
Esta funcin devuelve un entero ledo de un fichero binario. Su sintaxis es:
<num_entero> = getw (<var_fich>);
Donde:
num_entero es la variable que contendr el valor entero ledo del fichero.
var_fich es la variable declarada como FILE.
Metodologa de la Programacin II

103

Tema 2
Ejemplos:
i = getw (fich);
printf ("%d\n", getw (fich));
#include <stdio.h>
#include <stdlib.h>
#define FNAME "test.$$$"
int main(void)
{
FILE *fp;
int word;
/* place the word in a file */
fp = fopen(FNAME, "wb");
if (fp == NULL)
{
printf("Error opening file %s\n", FNAME);
exit(1);
}
word = 94;
putw(word,fp);
if (ferror(fp))
printf("Error writing to file\n");
else
printf("Successful write\n");
fclose(fp);
/* reopen the file */
fp = fopen(FNAME, "rb");
if (fp == NULL)
{
printf("Error opening file %s\n", FNAME);
exit(1);
}
/* extract the word */
word = getw(fp);
if (ferror(fp))
printf("Error reading file\n");
else
printf("Successful read: word = %d\n", word);
104

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


/* clean up */
fclose(fp);
unlink(FNAME);
return 0;
}
putw
Esta funcin escribe un entero en un fichero binario. Su sintaxis es:
putw (<num_entero>, <var_fich>);
Donde:
num_entero es el valor entero que se escribir en el fichero.
var_fich es la variable declarada como FILE.
Ejemplos:
putw (5, fich);
i = 99; putw (i, fich);
#include <stdio.h>
#include <stdlib.h>
#define FNAME "test.$$$"
int main(void)
{
FILE *fp;
int word;
/* place the word in a file */
fp = fopen(FNAME, "wb");
if (fp == NULL)
{
printf("Error opening file %s\n", FNAME);
exit(1);
}
word = 94;
putw(word,fp);
if (ferror(fp))
printf("Error writing to file\n");
else
printf("Successful write\n");
fclose(fp);
Metodologa de la Programacin II

105

Tema 2
/* reopen the file */
fp = fopen(FNAME, "rb");
if (fp == NULL)
{
printf("Error opening file %s\n", FNAME);
exit(1);
}
/* extract the word */
word = getw(fp);
if (ferror(fp))
printf("Error reading file\n");
else
printf("Successful read: word = %d\n", word);
/* clean up */
fclose(fp);
unlink(FNAME);
return 0;
}
fscanf
Esta funcin trabaja de la misma manera que scanf, pero leyendo los datos formateados de un
fichero. Su sintaxis es:
fscanf (<var_fich>, <cadena_de_control>, <lista_variables>);
Donde:
var_fich es la variable declarada como FILE.
cadena_de_control son las cadenas de control que se desean leer, tales como %d, %s, %f, %x,
etc...
lista_variables son las variables que contendrn los valores de la lectura del fichero que deben
coincidir con sus respectivas cadenas de control.
Ejemplos:
fscanf (fich, "%s%d%f", nombre, &edad, &altura);
#include <stdlib.h>
#include <stdio.h>
int main(void)
106

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


{
int i;
printf("Input an integer: ");
/* read an integer from the
standard input stream */
if (fscanf(stdin, "%d", &i))
printf("The integer read was: %i\n", i);
else
{
fprintf(stderr, "Error reading an integer from stdin.\n");
exit(1);
}
return 0;
}
fprintf
Esta funcin trabaja de la misma manera que printf, pero escribiendo los datos formateados sobre
un fichero. Su sintaxis es:
fprintf (<var_fich>, <cadena_de_control>, <lista_variables>);
Donde:
var_fich es la variable declarada como FILE.
cadena_de_control son las cadenas de control que se desean escribir, tales como %d, %s, %f, %x,
etc...
lista_variables son las variables que contendrn los valores para la escritura en el fichero que
deben coincidir con sus respectivas cadenas de control.
Ejemplos:
fprintf (fich, "%s%d%f", nombre, edad, altura);
#include <stdio.h>
int main()
{
FILE *fichero;
char nombre[10] = "datos.dat";
unsigned int i;
fichero = fopen( nombre, "w" );
printf( "Fichero: %s (para escritura) -> ", nombre );
if( fichero )
printf( "creado (ABIERTO)\n" );
else

Metodologa de la Programacin II

107

Tema 2
{
printf( "Error (NO ABIERTO)\n" );
return 1;
}
fprintf( fichero, "Esto es un ejemplo de usar la funcion \'fprintf\'\n" );
fprintf( fichero, "\t 2\t 3\t 4\n" );
fprintf( fichero, "x\tx\tx\tx\n\n" );
for( i=1; i<=10; i++ )
fprintf( fichero, "%d\t%d\t%d\t%d\n", i, i*i, i*i*i, i*i*i*i );
fprintf( stdout, "Datos guardados en el fichero: %s\n", nombre );
if( !fclose(fichero) )
printf( "Fichero cerrado\n" );
else
{
printf( "Error: fichero NO CERRADO\n" );
return 1;
}
return 0;
}

remove
Esta funcin borra fisicamente el archivo que se especifique. Devuelve un 0 si todo ha salido
correctamente o un -1 si ha ocurrido un error. Su sintaxis es:
<valor> = remove (<nombre_archivo>);
Donde:
valor es el valor que nos dir si ha ocurrido un error al borrar el fichero.
nombre_archivo es el nombre del fichero que se desea borrar, o sea, que se refiere a una cadena
de caracteres y no al identificador del fichero.
Ejemplos:
remove ("datos.dat");
strcpy (nombre, "texto.txt"); remove (nombre);
#include <stdio.h>
int main(void)
{
char file[80];
/* prompt for file name to delete */
printf("File to delete: ");
gets(file);
/* delete the file */
108

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


if (remove(file) == 0)
printf("Removed %s.\n",file);
else
perror("remove");
return 0;
}
rename
Esta funcin renombra un fichero especificado. Devuelve 0 si el cambio de nombre ha sido
correcto, y -1 si no lo ha sido. Su sintaxis es:
<valor> = rename (<nombre_actual>, <nuevo_nombre>);
Donde:
valor es el valor que nos dir si ha ocurrido un error al renombrar el fichero.
nombre_actual es el nombre actual del fichero que se desea renombrar. Se refiere a una cadena, y
no al identificador del fichero.
nuevo_nombre es el nuevo nombre que se le desea asignar al fichero. Se refiere a una cadena, y
no al identificador del fichero.
#include <stdio.h>
int main()
{
char viejo[18] = "fichero_viejo.tmp", nuevo[18] = "fichero_nuevo.tmp";
printf( "fichero viejo: %s", viejo );
if( rename(viejo, nuevo) == 0 )
printf( ", renombrado: %s\n", nuevo );
else
printf( "\nNo pudo ser renombrado\n" );
return 0;
}

Ejemplos:
strcpy (viejo, "clientes.xxx\0");
strcpy (nuevo, "clientes.dat\0);
rename (viejo, nuevo);
Ejemplo:
// copia.cpp: Copia de ficheros
// Uso: copia <fichero_origen> <fichero_destino>
#include <iostream.h>
#include <stdio.h>
Metodologa de la Programacin II

109

Tema 2
int main(int argc, char **argv)
{
FILE *fe, *fs;
unsigned char buffer[2048]; // Buffer de 2 Kbytes
int bytesLeidos;
if(argc != 3) {
cout << "Usar: copia <fichero_origen> <fichero_destino>" << endl;
return 1;
}
fe = fopen(argv[1], "rb"); // Abrir el fichero de entrada en lectura y binario
if(!fe) {
cout << "El fichero " << argv[1] << " no existe o no puede ser abierto." << endl;
return 1;
}
fs = fopen(argv[2], "wb"); // Crear o sobreescribir el fichero de salida en binario
if(!fs) {
cout << "El fichero " << argv[2] << " no puede ser creado." << endl;
fclose(fe);
return 1;
}
// Bucle de copia:
while((bytesLeidos = fread(buffer, 1, 2048, fe)))
fwrite(buffer, 1, bytesLeidos, fs);
// Cerrar ficheros:
fclose(fe);
fclose(fs);
return 0;
}
2.4 Gestin de un fichero secuencial de estructuras
Los componentes o registros de un fichero pueden ser de cualquier tipo de datos, desde tipos
bsicos (enteros, float, char, etc...) hasta estructuras de datos.
Las operaciones en cualquiera de los casos son siempre iguales. Veamos un ejemplo de un
fichero de estructuras:
Apertura:
...
struct Tcli
{
char nombre[30];
char direccin[30];
...
};
110

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


void main()
{
FILE *f;
struct Tcli cliente;
char fichero[13];
strcpy (fichero, "cliente.dat\0");
if ((fopen(fichero, "ab") == NULL)
{
printf ("Error al abrir el fichero\n");
exit (1);
}
...
}
Escritura:
fwrite (&cliente, sizeof(cliente), 1, f);
Lectura:
fread (&cliente, sizeof(cliente), 1, f);
Las operaciones bsicas en el mantenimiento o gestin de un fichero secuencial son las
siguientes:

Comprobacin de la existencia del fichero: Se abrir el fichero en modo lectura, si no


existe, se permitir la creacin del mismo, volvindolo a abrir en modo escritura.
Altas: Se abrir el fichero en modo aadir.
Bajas: Utilizaremos dos ficheros: el maestro y otro auxiliar. El maestro lo abriremos para
lectura, y el auxiliar para escritura. Iremos transfiriendo todos los registros del maestro al
auxiliar excepto el que queramos borrar. A continuacin, borraremos el maestro, y
asignaremos al auxiliar el nombre del maestro.
Modificaciones: Procederemos de igual forma que para las bajas, salvo que
transferiremos todos los registros al auxiliar, los no modificados y el modificado.
Borraremos el maestro y renombraremos el auxiliar para darle el nombre del maestro.
Consultas: Se abrir el fichero en modo lectura.
Listado por pantalla: Se abrir el fichero en modo lectura. Se ir leyendo informacin
mientras no se llegue al final del ficnero.
Listado por impresora: Utilizaremos dos ficheros, el maestro y el de impresora. El
primero lo abriremos para lectura, y el segundo para escritura, del siguiente modo:

FILE *f, *fimp;


f = fopen ("clientes.dat", "r");
fimp = fopen ("prn", "w");

Metodologa de la Programacin II

111

Tema 2
De tal forma que todo lo que escribamos en fimp saldr por impresora. Por tal motivo, debemos
pensar que se trata de un fichero de texto, por lo cual, usaremos funciones de escritura en ficheros
de texto.
2.5 Acceso directo a ficheros
Ya hemos visto como acceder secuencialmente a un fichero, sin embargo tambin se puede hacer
de forma directa.
Supongamos que tenemos definido un fichero con la siguiente estructura de registro:
struct
{
int codigo;
char nomart[31];
float precio;
}articulo;
Es evidente que la longitud de cada registro es de 37 bytes (2+31+4 bytes). De esta forma, la
disposicin de los registros dentro del fichero en disco se realiza en las siguientes posiciones:

El acceso directo consiste en indicar la posicin a la que queremos acceder en bytes. Por ejemplo,
para acceder directamente al registro 2, indicaremos que queremos ir al byte 37, contando desde
el principio del registro. La orden que posibilita este acceso es la siguiente.
fseek

112

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


<valor> = fseek (<var_fich>, <direccin>, <desde>);
Donde:
valor es el valor que nos dir si ha ocurrido un error al desplazarnos por el fichero. 0 si todo ha
ido bien.
var_fich es el puntero al fichero que estamos utilizando.
direccin es el nmero de bytes que vamos a desplazarnos.
desde es el punto de partida del desplazamiento. Este punto admite tres posibles valores:
Referencia

Valor Desde

SEEK_SET

Principio de fichero

SEEK_CUR

Posicin actual del fichero

SEEK_END

Final del fichero

Hay que tener muy en cuenta que no es lo mismo desplazarse 37 bytes desde el principio del
fichero (accederemos al segundo registro), que desde el lugar donde nos encontremos
(accederemos al siguiente registro)
Existe otra funcin que nos devuelve la posicin en la que nos encontramos dentro del fichero
#include <stdio.h>
long filesize(FILE *stream);
int main(void)
{
FILE *stream;
stream = fopen("MYFILE.TXT", "w+");
fprintf(stream, "This is a test");
printf("Filesize of MYFILE.TXT is %ld bytes\n", filesize(stream));
fclose(stream);
return 0;
}
long filesize(FILE *stream)
{
long curpos, length;
curpos = ftell(stream);
fseek(stream, 0L, SEEK_END);
length = ftell(stream);
fseek(stream, curpos, SEEK_SET);
Metodologa de la Programacin II

113

Tema 2
return length;
}
ftell
<posicin> = ftell (<var_fich>);
Donde:
posicin es la variable que contendr la posicin en bytes en la que nos encontramos en ese
momento en el fichero.
var_fich es la variable declarada como FILE.
Esta funcin se suele usar, a parte de para saber la situacin exacta en el fichero, para obtener la
longitud del mismo. Veamos un ejemplo:
...
if ((f=fopen("articul.dat", "rb")) == NULL)
{
printf ("Imposible crear fichero\n");
exit (1);
}
fseek (f, 0, 2); /* tambien puede ser fseek (f, 2, SEEK_END); */
l = ftell(f);
printf ("El fichero tiene un tamao de %ld bytes\n", l);
printf ("Y un total de %ld registros\n", l/sizeof(reg));
/* donde reg sera la estructura o el dato simple contenido en el fichero */
...
La forma de dar de alta registros es igual que para el acceso secuencial. Se har un recorrido
hasta llegar al final del fichero para comprobar que no existe el cdigo del registro a dar de alta, y
en ese caso, se procede a hacer fwrite. Abriremos para ello el fichero en modo aadir.
#include <stdio.h>
#include <string.h>
int main()
{
char nombre[11] = "datos4.dat", mensaje[81]="";
FILE *fichero;
long int comienzo, final;
fichero = fopen( nombre, "r" );
printf( "Fichero: %s -> ", nombre );
if( fichero )
printf( "existe (ABIERTO)\n" );
else
{
printf( "Error (NO ABIERTO)\n" );
return 1;
}

114

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


if( (comienzo=ftell( fichero )) < 0 )
printf( "ERROR: ftell no ha
funcionado\n" );
else
printf( "Posicion del fichero: %d\n\n", comienzo );
fseek( fichero, 0L, SEEK_END );
final = ftell( fichero );
fseek( fichero, 0L, SEEK_SET );
fgets( mensaje, 80, fichero );
printf( "Tamao del fichero \"%s\": %d bytes\n", nombre, final-comienzo+1 );
printf( "Mensaje del fichero:\n%s\n", mensaje );
printf( "\nTamao del mensaje (usando strlen): %d\n", strlen(mensaje) );
if( !fclose(fichero) )
printf( "Fichero cerrado\n" );
else
{
printf( "Error: fichero NO CERRADO\n" );
return 1;
}
return 0;
}

Las bajas consisten en localizar el registro segn un cdigo, y escribir un registro vaco en tal
posicin. Para ello tendremos que abrir el fichero en modo lectura/escritura, o sea, "r+".
Las modificaciones, de forma similar a las bajas pero escribiendo en el lugar correspondiente el
registro modificado que reemplace al actual.
Las consultas no son otra cosa que un acceso directo al cdigo indicado.
Y por ltimo los listados, que consisten en un recorrido secuencial desde el primer registro hasta
el ltimo.
Recordaros que la forma de gestin de los ficheros aqu expuesta es la ms bsica que yo
conozco, pero que se puede hacer de mil maneras, mejores o peores, pero que no es la nica.
Comentamos casi al principio que C no soporta los ficheros indexados, pero puedes crearlos, slo
debes pensar en crearos un fichero de ndices y otro maestro y a partir de ah, todo sale solo.
Muy a menudo necesitamos almacenar cierta cantidad de datos de forma ms o menos
permanente. La memoria del ordenador es volatil, y lo que es peor, escasa y cara. De modo que
cuando tenemos que guardar nuestros datos durante cierto tiempo tenemos que recurrir a sistemas
de almacenamiento ms econmicos, aunque sea a costa de que sean ms lentos.
Durante la historia de los ordenadores se han usado varios mtodos distintos para el
almacenamiento de datos. Al principio se recurri a cintas de papel perforadas, despus a tarjetas
perforadas. A continuacin se pas al soporte magntico, empezando por grandes rollos de cintas
abiertas.
Hasta aqu, todos los sistemas de almacenamiento externo eran secuenciales, es decir, no
permitan acceder al punto exacto donde se guardaba la informacin sin antes haber partido del
Metodologa de la Programacin II

115

Tema 2
principio y sin haber ledo toda la informacin desde el principio hasta el punto donde se
encontraba la que estabamos buscando.
Con las cintas magnticas empez lo que con el tiempo sera el acceso aleatorio a los datos. Se
poda reservar parte de la cinta para guardar cierta informacin sobre la situacin de los datos, y
aadir ciertas marcas que hicieran ms sencillo localizarla.
Pero no fu hasta la aparicin de los discos magnticos cuando sta tcnica lleg a su apogeo. En
los discos es ms sencillo acceder a cualquier punto de la superficie en poco tiempo, ya que se
accede al punto de lectura y escritura usando dos coordenadas fsicas. Por una parte la cabeza de
lectura/escritura se puede mover en el sentido del radio del disco, y por otra el disco gira
permanentemente, con lo que cualquier punto del disco pasa por la cabeza en un tiempo
relativamente corto. Esto no pasa con las cintas, donde slo hay una coordenada fsica.
Con la invencin y proliferacin de los discos se desarrollaron los ficheros de acceso aleatorio,
que permiten acceder a cualquier dato almacenado en un fichero en relativamente poco tiempo.
Actualmente, los discos duros tienen una enorme capacidad y son muy rpidos, aunque an
siguen siendo lentos, en comparacin con las memorias RAM. El caso de los CD es algo
intermedio. En realidad son secuenciales en cuanto al modo de guardar los datos, cada disco slo
tiene una pista de datos grabada en espiral. Sin embargo, este sistema, combinado con algo de
memoria RAM, proporciona un acceso muy prximo al de los discos duros.
En cuanto al tipo de acceso, en C y C++ podemos clasificar los archivos segn varias categoras:
1. Dependiendo de la direccin del flujo de datos:
De entrada: los datos se leen por el programa desde el archivo.
De salida: los datos se escriben por el programa hacia el archivo.
De entrada/salida: los datos pueden se escritos o ledos.
2. Dependiendo del tipo de valores permitidos a cada byte:
De texto: slo estn permitidos ciertos rangos de valores para cada byte. Algunos
bytes tienen un significado especial, por ejemplo, el valor hexadecimal 0x1A marca el
fin de fichero. Si abrimos un archivo en modo texto, no ser posible leer ms all de
ese byte, aunque el fichero sea ms largo.
Binarios: estn permitidos todos lo valores para cada byte. En estos archivos el final
del fichero se detecta de otro modo, dependiendo del soporte y del sistema operativo.
La mayora de las veces se hace guardando la longitud del fichero. Cuando queramos
almacenar valores enteros, o en coma flotante, o imgenes, etc, deberemos usar este
tipo de archivos.
3. Segn el tipo de acceso:
Archivos secuenciales: imitan el modo de acceso de los antiguos ficheros secuenciales
almacenados en cintas magnticas y
Archivos de acceso aleatorio: permiten acceder a cualquier punto de ellos para realizar
lecturas y/o escrituras.
4. Segn la longitud de registro:
Longitud variable: en realidad, en este tipo de archivos no tiene sentido hablar de
longitud de registro, podemos considerar cada byte como un registro. Tambin puede
suceder que nuestra aplicacin conozca el tipo y longitud de cada dato almacenado en
116

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos

el archivo, y lea o escriba los bytes necesarios en cada ocasin. Otro caso es cuando se
usa una marca para el final de registro, por ejemplo, en ficheros de texto se usa el
carcter de retorno de lnea para eso. En estos casos cada registro es de longitud
diferente.
Longitud constante: en estos archivos los datos se almacenan en forma de registro de
tamao contante. En C usaremos estructuras para definir los registros. C dispone de
funciones de librera adecuadas para manejar este tipo de ficheros.
Mixtos: en ocasiones pueden crearse archivos que combinen los dos tipos de registros,
por ejemplo, dBASE usa registros de longitud constante, pero aade un registro
especial de cabecera al principio para definir, entre otras cosas, el tama y el tipo de
los registros.

Es posible crear archivos combinando cada una de estas categoras, por ejemplo: archivos
secuenciales de texto de longitud de registro variable, que son los tpicos archivos de texto.
Archivos de acceso aleatorio binarios de longitud de registro constante, normalmente usados en
bases de datos. Y tambin cualquier combinacin menos corriente, como archivos secuenciales
binarios de longitud de registro constante, etc.
En cuanto a cmo se definen estas propiedades, hay dos casos. Si son binarios o de texto o de
entrada, salida o entrada/salida, se define al abrir el fichero, mediante la funcin fopen:
La funcin open usa dos parmetros. El primero es el nombre del fichero que contiene el archivo.
El segundo es em modo que es una cadena que indica el modo en que se abrir el archivo: lectura
o escritura, y el tipo de datos que contiene: de texto o binarios.
En C, los ficheros admiten seis modos en cuanto a la direccin del flujo de datos:
r: slo lectura. El fichero debe existir.
w: se abre para escritura, se crea un fichero nuevo o se sobreescribe si ya existe.
a: aadir, se abre para escritura, el cursor se situa al final del fichero. Si el fichero no
existe, se crea.
r+: lectura y escritura. El fichero debe existir.
w+: lectura y escritura, se crea un fichero nuevo o se sobreescribe si ya existe.
a+: aadir, lectura y escritura, el cursor se situa al final del fichero. Si el fichero no existe,
se crea.
En cuanto a los valores permitidos para los bytes, se puede aadir otro carcter a la cadena de
modo:
t: modo texto. Normalmente es el modo por defecto. Se suele omitir.
b: modo binario.
Ejemplo:
A continuacin se incluye un ejemplo de un programa que trabaja con registros de acceso
aleatorio, es un poco largo, pero bastante completo:
// alea.cpp: Ejemplo de ficheros de acceso aleatorio.
#include <stdio.h>
Metodologa de la Programacin II

117

Tema 2
#include <stdlib.h>
struct stRegistro {
char valido; // Campo que indica si el registro es vlido S->Vlido, N->Invlido
char nombre[34];
int dato[4];
};
int Menu();
void Leer(stRegistro &reg);
void Mostrar(stRegistro &reg);
void Listar(long n, stRegistro &reg);
long LeeNumero();
void Empaquetar(FILE *fa);
int main()
{
stRegistro reg;
FILE *fa;
int opcion;
long numero;
fa = fopen("alea.dat", "r+b");
// Este modo permite leer y escribir
if(!fa) fa = fopen("alea.dat", "w+b"); // si el fichero no existe, lo crea.
do {
opcion = Menu();
switch(opcion) {
case '1': // Aadir registro
Leer(reg);
// Insertar al final:
fseek(fa, 0, SEEK_END);
fwrite(&reg, sizeof(stRegistro), 1, fa);
break;
case '2': // Mostrar registro
system("cls");
printf("Mostrar registro: ");
numero = LeeNumero();
fseek(fa, numero*sizeof(stRegistro), SEEK_SET);
fread(&reg, sizeof(stRegistro), 1, fa);
Mostrar(reg);
break;
case '3': // Eliminar registro
system("cls");
printf("Eliminar registro: ");
numero = LeeNumero();
fseek(fa, numero*sizeof(stRegistro), SEEK_SET);
fread(&reg, sizeof(stRegistro), 1, fa);
reg.valido = 'N';
fseek(fa, numero*sizeof(stRegistro), SEEK_SET);
fwrite(&reg, sizeof(stRegistro), 1, fa);
break;
case '4': // Mostrar todo
118

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


rewind(fa);
numero = 0;
system("cls");
printf("Nombre
Datos\n");
while(fread(&reg, sizeof(stRegistro), 1, fa)) Listar(numero++, reg);
system("PAUSE");
break;
case '5': // Eliminar marcados
Empaquetar(fa);
break;
}
} while(opcion != '0');
fclose(fa);
return 0;
}
// Muestra un men con las opciones disponibles y captura una opcin del usuario
int Menu()
{
char resp[20];
do {
system("cls");
printf("MENU PRINCIPAL\n");
printf("--------------\n\n");
printf("1- Insertar registro\n");
printf("2- Mostrar registro\n");
printf("3- Eliminar registro\n");
printf("4- Mostrar todo\n");
printf("5- Eliminar registros marcados\n");
printf("0- Salir\n");
fgets(resp, 20, stdin);
} while(resp[0] < '0' && resp[0] > '5');
return resp[0];
}
// Permite que el usuario introduzca un registro por pantalla
void Leer(stRegistro &reg)
{
int i;
char numero[6];
system("cls");
printf("Leer registro:\n\n");
reg.valido = 'S';
printf("Nombre: ");
fgets(reg.nombre, 34, stdin);
// la funcin fgets captura el retorno de lnea, hay que eliminarlo:
for(i = strlen(reg.nombre)-1; i && reg.nombre[i] < ' '; i--) reg.nombre[i] = 0;
for(i = 0; i < 4; i++) {
printf("Dato[%1d]: ", i);
fgets(numero, 6, stdin);
Metodologa de la Programacin II

119

Tema 2
reg.dato[i] = atoi(numero);
}
}
// Muestra un registro en pantalla, si no est marcado como borrado
void Mostrar(stRegistro &reg)
{
int i;
system("cls");
if(reg.valido == 'S') {
printf("Nombre: %s\n", reg.nombre);
for(i = 0; i < 4; i++) printf("Dato[%1d]: %d\n", i, reg.dato[i]);
}
system("PAUSE");
}
// Muestra un registro por pantalla en forma de listado,
// si no est marcado como borrado
void Listar(long n, stRegistro &reg)
{
int i;
if(reg.valido == 'S') {
printf("[%6ld] %-34s", n, reg.nombre);
for(i = 0; i < 4; i++) printf(", %4d", reg.dato[i]);
printf("\n");
}
}
// Lee un nmero suministrado por el usuario
long LeeNumero()
{
char numero[6];
fgets(numero, 6, stdin);
return atoi(numero);
}
// Elimina los registros marcados como borrados
void Empaquetar(FILE *fa)
{
FILE *ftemp;
stRegistro reg;
ftemp = fopen("alea.tmp", "wb");
rewind(fa);
while(fread(&reg, sizeof(stRegistro), 1, fa))
if(reg.valido == 'S') fwrite(&reg, sizeof(stRegistro), 1, ftemp);
fclose(ftemp);
fclose(fa);
remove("alea.bak");
rename("alea.dat", "alea.bak");
rename("alea.tmp", "alea.dat");
fa = fopen("alea.dat", "r+b");
}
120

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


2.6 Ordenar ficheros secuenciales
A veces necesitaremos ordenar el contenido de un fichero secuencial, ya sea de longitud de
registro variable o constante.
Debido a la naturaleza de estos archivos, en general no ser posible usar los mtodos de
ordenamiento que usaramos con tablas en memoria. En muchas ocasiones trabajaremos con
archivos muy grandes, de modo que ser imposible ordenarlos en memoria y despus
reconstruirlos en disco.
Ejemplo:
// mezcla.cpp : Ordenamiento de archivos secuenciales
// Ordena ficheros de texto por orden alfabtico de lneas
// Usando el algoritmo de mezcla natural
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Mostrar(FILE *fich);
void Mezcla(FILE *fich);
void Separar(FILE *fich, FILE **aux);
bool Mezclar(FILE *fich, FILE **aux);
int main()
{
FILE *fichero;
fichero = fopen("mezcla.txt", "r+");
puts("Fichero desordenado\n");
Mostrar(fichero);
puts("Ordenando fichero\n");
Mezcla(fichero);
puts("Fichero ordenado\n");
Mostrar(fichero);
fclose(fichero);
system("PAUSE");
return 0;
}
// Muestra el contenido del fichero "fich"
void Mostrar(FILE *fich)
{
char linea[128];
rewind(fich);
fgets(linea, 128, fich);
while(!feof(fich)) {
puts(linea);
Metodologa de la Programacin II

121

Tema 2
fgets(linea, 128, fich);
}
}
// Algoritmo de mezcla:
void Mezcla(FILE *fich)
{
bool ordenado;
FILE *aux[2];
// Bucle que se repite hasta que el fichero est ordenado:
do {
// Crea los dos ficheros auxiliares para separar los tramos:
aux[0] = fopen("aux1.txt", "w+");
aux[1] = fopen("aux2.txt", "w+");
rewind(fich);
Separar(fich, aux);
rewind(aux[0]);
rewind(aux[1]);
rewind(fich);
ordenado = Mezclar(fich, aux);
fclose(aux[0]);
fclose(aux[1]);
} while(!ordenado);
// Elimina los ficheros auxiliares:
remove(aux[0]);
remove(aux[1]);
}
// Separa los tramos ordenados alternando entre los ficheros auxiliares:
void Separar(FILE *fich, FILE **aux)
{
char linea[128], anterior[2][128];
int salida = 0;
// Volores iniciales para los ltimos valores
// almacenados en los ficheros auxiliares
strcpy(anterior[0], "");
strcpy(anterior[1], "");
// Captura la primero lnea:
fgets(linea, 128, fich);
while(!feof(fich)) {
// Decide a qu fichero de salida corresponde la lnea leda:
if(salida == 0 && strcmp(linea, anterior[0]) < 0) salida = 1;
else if(salida == 1 && strcmp(linea, anterior[1]) < 0) salida = 0;
// Almacena la lnea actual como la ltima aadida:
strcpy(anterior[salida], linea);
// Aade la lnea al fichero auxiliar:
fputs(linea, aux[salida]);
122

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


// Lee la siguiente lnea:
fgets(linea, 128, fich);
}
}
// Mezcla los ficheros auxiliares:
bool Mezclar(FILE *fich, FILE **aux)
{
char ultima[128], linea[2][128], anterior[2][128];
int entrada;
int tramos = 0;
// Lee la primera lnea de cada fichero auxiliar:
fgets(linea[0], 128, aux[0]);
fgets(linea[1], 128, aux[1]);
// Valores iniciales;
strcpy(ultima, "");
strcpy(anterior[0], "");
strcpy(anterior[1], "");
// Bucle, mientras no se acabe ninguno de los ficheros auxiliares (quedan tramos por mezclar):
while(!feof(aux[0]) && !feof(aux[1])) {
// Selecciona la lnea que se aadir:
if(strcmp(linea[0], linea[1]) <= 0) entrada = 0; else entrada = 1;
// Almacena el valor como el ltimo aadido:
strcpy(anterior[entrada], linea[entrada]);
// Aade la lnea al fichero:
fputs(linea[entrada], fich);
// Lee la siguiente lnea del fichero auxiliar:
fgets(linea[entrada], 128, aux[entrada]);
// Verificar fin de tramo, si es as copiar el resto del otro tramo:
if(strcmp(anterior[entrada], linea[entrada]) >= 0) {
entrada == 0 ? entrada = 1 : entrada = 0;
tramos++;
// Copia lo que queda del tramo actual al fichero de salida:
do {
strcpy(anterior[entrada], linea[entrada]);
fputs(linea[entrada], fich);
fgets(linea[entrada], 128, aux[entrada]);
} while(!feof(aux[entrada]) && strcmp(anterior[entrada], linea[entrada]) <= 0);
}
}
// Aadir tramos que queden sin mezclar:
if(!feof(aux[0])) tramos++;
while(!feof(aux[0])) {
fputs(linea[0], fich);
fgets(linea[0], 128, aux[0]);
}
if(!feof(aux[1])) tramos++;
Metodologa de la Programacin II

123

Tema 2
while(!feof(aux[1])) {
fputs(linea[1], fich);
fgets(linea[1], 128, aux[1]);
}
return(tramos == 1);
}
Ordenar archivos es siempre una tarea muy lenta y requiere mucho tiempo. Este algoritmo,
adems requiere el doble de espacio en disco del que ocupa el fichero a ordenar, por ejemplo,
para ordenar un fichero de 500 megas se necesitan otros 500 megas de disco libres.
Sin embargo, un fichero como el mencionado, sera muy difcil de ordenar en memoria.
2.7 Ordenar ficheros de acceso aleatorio
Cuando trabajemos con ficheros de acceso secuencial con tamao de registro constante,
podremos aplicar los mismos algoritmos de ordenacin que con tablas en memoria, ya que es
posible acceder a cada registro para lectura y escritura.
En el caso de ficheros de acceso aleatorio con tamao de registro variable, los trataremos como si
fueran secuenciales.
Algoritmo Quicksort. Por supuesto, hay que elegir un algoritmo que impleque un mnimo de
lecturas y escrituras en el fichero, y preferentemente, que stas operaciones estn los ms
prximas posible entre si. Resulta muy costoso, en trminos de tiempo de ejecucin, hacer
muchas lecturas y escrituras en disco, y ms si los puntos donde se realizan estn muy separados
entre ellos.
Como ejemplo, usaremos el algoritmo de ordenacin quicksort, adaptndolo para ordenar
ficheros.
Usaremos el programa de ejemplo que usamos para los archivos de acceso aleatorio "alea.cpp". Y
aadiremos una nueva opcin para ordenar el archivo.
Ejemplo:
// alea2.cpp: Ejemplo de ficheros de acceso aleatorio.
// Incluye la opcin de ordenar el archivo.
#include <stdio.h>
#include <stdlib.h>
struct stRegistro {
char valido; // Campo que indica si el registro es valido S->Vlido, N->Invlido
char nombre[34];
int dato[4];
};
int Menu();
void Leer(stRegistro &reg);
void Mostrar(stRegistro &reg);
124

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


void Listar(long n, stRegistro &reg);
long LeeNumero();
void Empaquetar(FILE *fa);
void Ordenar(FILE *fa);
void Intercambia(FILE *fa, long iz, long de);
char *LeeCampo(FILE *fa, long n, char *buf);
void QuickSort(FILE *fa, long inicio, long final);
int main()
{
stRegistro reg;
FILE *fa;
int opcion;
long numero;
fa = fopen("alea.dat", "r+b");
// Este modo permite leer y escribir
if(!fa) fa = fopen("alea.dat", "w+b"); // si el fichero no existe, lo crea.
do {
opcion = Menu();
switch(opcion) {
case '1': // Aadir registro
Leer(reg);
// Insertar al final:
fseek(fa, 0, SEEK_END);
fwrite(&reg, sizeof(stRegistro), 1, fa);
break;
case '2': // Mostrar registro
system("cls");
printf("Mostrar registro: ");
numero = LeeNumero();
fseek(fa, numero*sizeof(stRegistro), SEEK_SET);
fread(&reg, sizeof(stRegistro), 1, fa);
Mostrar(reg);
break;
case '3': // Eliminar registro
system("cls");
printf("Eliminar registro: ");
numero = LeeNumero();
fseek(fa, numero*sizeof(stRegistro), SEEK_SET);
fread(&reg, sizeof(stRegistro), 1, fa);
reg.valido = 'N';
fseek(fa, numero*sizeof(stRegistro), SEEK_SET);
fwrite(&reg, sizeof(stRegistro), 1, fa);
break;
case '4': // Mostrar todo
rewind(fa);
numero = 0;
system("cls");
Metodologa de la Programacin II

125

Tema 2
printf("Nombre
Datos\n");
while(fread(&reg, sizeof(stRegistro), 1, fa)) Listar(numero++, reg);
system("PAUSE");
break;
case '5': // Eliminar marcados
Empaquetar(fa);
break;
case '6': // Ordenar
Empaquetar(fa);
Ordenar(fa);
break;
}
} while(opcion != '0');
fclose(fa);
return 0;
}
// Muestra un men con las opciones disponibles y captura una opcin del usuario
int Menu()
{
char resp[20];
do {
system("cls");
printf("MENU PRINCIPAL\n");
printf("--------------\n\n");
printf("1- Insertar registro\n");
printf("2- Mostrar registro\n");
printf("3- Eliminar registro\n");
printf("4- Mostrar todo\n");
printf("5- Eliminar registros marcados\n");
printf("6- Ordenar fichero\n");
printf("0- Salir\n");
fgets(resp, 20, stdin);
} while(resp[0] < '0' && resp[0] > '6');
return resp[0];
}
// Permite que el usuario introduzca un registro por pantalla
void Leer(stRegistro &reg)
{
int i;
char numero[6];
system("cls");
printf("Leer registro:\n\n");
reg.valido = 'S';
printf("Nombre: ");
fgets(reg.nombre, 34, stdin);
126

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


// la funcin fgets captura el retorno de lnea, hay que eliminarlo:
for(i = strlen(reg.nombre)-1; i && reg.nombre[i] < ' '; i--) reg.nombre[i] = 0;
for(i = 0; i < 4; i++) {
printf("Dato[%1d]: ", i);
fgets(numero, 6, stdin);
reg.dato[i] = atoi(numero);
}
}
// Muestra un registro en pantalla, si no est marcado como borrado
void Mostrar(stRegistro &reg)
{
int i;
system("cls");
if(reg.valido == 'S') {
printf("Nombre: %s\n", reg.nombre);
for(i = 0; i < 4; i++) printf("Dato[%1d]: %d\n", i, reg.dato[i]);
}
system("PAUSE");
}
// Muestra un registro por pantalla en forma de listado,
// si no est marcado como borrado
void Listar(long n, stRegistro &reg)
{
int i;
if(reg.valido == 'S') {
printf("[%6ld] %-34s", n, reg.nombre);
for(i = 0; i < 4; i++) printf(", %4d", reg.dato[i]);
printf("\n");
}
}
// Lee un nmero suministrado por el usuario
long LeeNumero()
{
char numero[6];
fgets(numero, 6, stdin);
return atoi(numero);
}
// Elimina los registros marcados como borrados
void Empaquetar(FILE *fa)
{
FILE *ftemp;
stRegistro reg;
Metodologa de la Programacin II

127

Tema 2
ftemp = fopen("alea.tmp", "wb");
rewind(fa);
while(fread(&reg, sizeof(stRegistro), 1, fa))
if(reg.valido == 'S') fwrite(&reg, sizeof(stRegistro), 1, ftemp);
fclose(ftemp);
fclose(fa);
remove("alea.bak");
rename("alea.dat", "alea.bak");
rename("alea.tmp", "alea.dat");
fa = fopen("alea.dat", "r+b");
}
void Ordenar(FILE *fa)
{
long nRegs;
fseek(fa, 0, SEEK_END);
nRegs = ftell(fa)/sizeof(stRegistro);
QuickSort(fa, 0L, nRegs-1);
}
void QuickSort(FILE *fa, long inicio, long final)
{
long iz, de;
char mitad[34];
static char cad[34];
iz = inicio;
de = final;
strcpy(mitad, LeeCampo(fa, (iz+de)/2, cad));
do {
while(strcmp(LeeCampo(fa, iz, cad), mitad) < 0 && iz < final) iz++;
while(strcmp(mitad, LeeCampo(fa, de, cad)) < 0 && de > inicio) de--;
if(iz < de) Intercambia(fa, iz, de);
if(iz <= de) {
iz++;
de--;
}
} while(iz <= de);
if(inicio < de) QuickSort(fa, inicio, de);
if(iz < final) QuickSort(fa, iz, final);
}
char *LeeCampo(FILE *fa, long n, char *buf)
{
stRegistro reg;
fseek(fa, n*sizeof(stRegistro), SEEK_SET);
128

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


fread(&reg, sizeof(stRegistro), 1, fa);
strcpy(buf, reg.nombre);
return buf;
}
void Intercambia(FILE *fa, long iz, long de)
{
stRegistro reg1, reg2;
fseek(fa, iz*sizeof(stRegistro), SEEK_SET);
fread(&reg1, sizeof(stRegistro), 1, fa);
fseek(fa, de*sizeof(stRegistro), SEEK_SET);
fread(&reg2, sizeof(stRegistro), 1, fa);
fseek(fa, iz*sizeof(stRegistro), SEEK_SET);
fwrite(&reg2, sizeof(stRegistro), 1, fa);
fseek(fa, de*sizeof(stRegistro), SEEK_SET);
fwrite(&reg1, sizeof(stRegistro), 1, fa);
}
El algoritmo que hemos usado es bastante bueno para ordenar ficheros, ya que requiere muy
pocos intercambios de registros, pero de todos modos, con ficheros grandes puede ser un proceso
muy lento. En general es preferible no ordenar los ficheros, salvo que sea muy necesario.
2.8 Ficheros de ndices
Mantener grandes ficheros de datos ordenados es muy costoso, ya que requiere mucho tiempo de
procesador. Afortunadamente, existe una alternativa mucho mejor: indicarlos (o indexarlos).
Para indicar un archivo normalmente se suele generar un archivo auxiliar de ndices. Existen
varios mtodos, de los que veremos algunos. El ms sencillo es crear un archivo plano que slo
contenga registros con dos campos: el campo o la expresin por la que queremos ordenar el
archivo, y un campo con un ndexe que almecene la posicin del registro indicado en el archivo
de datos.
Por ejemplo, supongamos que tenemos un archivo de datos con la siguiente estructura de registro:
struct stRegistro {
char nombre[32];
char apellido[2][32];
char telefono[12];
char calle[45];
int numero;
char ciudad[32];
char fechaNacimiento[9]; // formato AAAAMMDD: Ao, mes y da
char estadoCivil;
int hijos;
}

Metodologa de la Programacin II

129

Tema 2
Imaginemos que necesitamos buscar un registro a partir del nmero de telfono. Si no tenemos el
archivo ordenado por ese campo, estaremos obligados a leer todos los registros del archivo hasta
encontrar el que buscamos, y si el nmero no est, tendremos que leer todos los registros que
existan.
Si tenemos el archivo ordenado por nmeros de telfono podremos aplicar un algoritmo de
bsqueda. Pero si tambin queremos hacer bsquedas por otros campos, estaremos obligados a
ordenar de nuevo el archivo.
La solucin es crear un fichero de ndices, cada registro de este archivo tendr la siguiente
estructura:
struct stIndiceTelefono {
char telefono[12];
long indice;
}
Crearemos el fichero de ndices a partir del archivo de datos, asignando a cada registro el campo
"telefono" y el nmero de registro correspondiente. Veamos un ejemplo:
000: [Fulanito] [Prez] [Sanchez] [12345678] [Mayor] [15] [Lisboa] [19540425] [S] [0]
001: [Fonforito] [Fernandez] [Lpez] [84565456] [Baja] [54] [Londres] [19750924] [C] [3]
002: [Tantolito] [Jimenez] [Fernandez] [45684565] [Alta] [153] [Berlin] [19840628] [S] [0]
003: [Menganito] [Sanchez] [Lpez] [23254532] [Diagonal] [145] [Barcelona] [19650505] [C]
[1]
004: [Tulanito] [Sanz] [Sanchez] [54556544] [Pez] [18] [Dubln] [19750111] [S] [0]
Generamos un fichero de ndices:
[12345678][000]
[84565456][001]
[45684565][002]
[23254532][003]
[54556544][004]
Y lo ordenamos:
[12345678][000]
[23254532][003]
[45684565][002]
[54556544][004]
[84565456][001]
Ahora, cuando queramos buscar un nmero de telfono, lo haremos en el fichero de ndices, por
ejemplo el "54556544" ser el registro nmero 3, y le corresponde el ndice "004". Con ese ndice
podemos acceder directamente al archivo de datos, y veremos que el nmero corresponde a
"Tulanito Sanz Sanchez".
Por supuesto, nada nos impide tener ms ficheros de ndices, para otros campos.
130

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


El mayor problema es mantener los ficheros de ndices ordenados a medida que aadimos,
eliminamos o modificamos registros. Pero al ser los registros de ndices ms pequeos, los
ficheros son ms manejables, pudiendo incluso almacenarse en memoria en muchos casos.
Ejemplo
Veramos un ejemplo de implementacin de ndices:
// indices.cpp: Ejemplo de ficheros de acceso aleatorio con ndices.
#include <stdio.h>
#include <stdlib.h>
struct stRegistro {
char valido; // Campo que indica si el registro es valido S->Vlido, N->Invlido
char nombre[34];
char apellido[2][34];
char telefono[10];
};
struct stIndice {
char telefono[10];
long indice;
};
int Menu();
void Capturar(stRegistro &reg);
void EliminarRetornoLinea(char *cad);
void Leer(FILE *fa, stRegistro &:reg, char *telefono);
void Insertar(FILE *fa, stRegistro &reg);
void Mostrar(stRegistro &reg);
void ListarPorTelefonos(FILE *fa);
void ListarNatural(FILE *fa);
void ReconstruirIndices(FILE *fa);
// Funciones para ordenar el fichero de ndices:
void Intercambia(FILE *fa, long iz, long de);
char *LeeCampo(FILE *fa, long n, char *buf);
void QuickSort(FILE *fa, long inicio, long final);
int main()
{
stRegistro reg;
FILE *fa;
int opcion;
char telefono[10];
fa = fopen("indices.dat", "r+b);
// Este modo permite leer y escribir
if(!fa) fa = fopen("indices.dat", "w+b"); // si el fichero no existe, lo crea.
do {
Metodologa de la Programacin II

131

Tema 2
opcion = Menu();
switch(opcion) {
case '1': // Insertar registro
Capturar(reg);
Insertar(fa, reg);
break;
case '2': // Buscar registro
system("cls");
printf("Buscar registro: ");
do {
fgets(telefono, 10, stdin);
EliminarRetornoLinea(telefono);
} while(strlen(telefono) < 1);
Leer(fa, reg, telefono);
Mostrar(reg);
break;
case '3': // Indicar archivo
system("cls");
printf("Indicando archivo: ");
ReconstruirIndices(fa);
break;
case '4': // Mostrar todo por orden de telfonos
ListarPorTelefonos(fa);
break;
case '5': // Mostrar todo por orden natural
ListarNatural(fa);
break;
}
} while(opcion != '0');
fclose(fa);
return 0;
}
// Muestra un men con las opciones disponibles y captura una opcin del usuario
int Menu()
{
char resp[20];
do {
system("cls");
printf("MENU PRINCIPAL\n");
printf("--------------\n\n");
printf("1- Insertar registro\n");
printf("2- Buscar registro\n");
printf("3- Reindicar archivo\n");
printf("4- Listar por orden de telfonos\n");
printf("5- Listar por orden natural\n");
printf("0- Salir\n");
fgets(resp, 20, stdin);
132

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


} while(resp[0] < '0' && resp[0] > '5');
return resp[0];
}
// Permite que el usuario introduzca un registro por pantalla
void Capturar(stRegistro &reg)
{
int i;
char numero[6];
system("cls");
printf("Leer registro:\n\n");
reg.valido = 'S';
printf("Nombre: ");
fgets(reg.nombre, 34, stdin);
EliminarRetornoLinea(reg.nombre);
printf("Primer apellido: ");
fgets(reg.apellido[0], 34, stdin);
EliminarRetornoLinea(reg.apellido[0]);
printf("Segundo apellido: ");
fgets(reg.apellido[1], 34, stdin);
EliminarRetornoLinea(reg.apellido[1]);
printf("Telfono: ");
fgets(reg.telefono, 10, stdin);
EliminarRetornoLinea(reg.telefono);
}
// Elimina los caracteres de retorno de lnea al final de cadena
void EliminarRetornoLinea(char *cad)
{
int i;
// la funcin fgets captura el retorno de lnea, hay que eliminarlo:
for(i = strlen(cad)-1; i >= 0 && cad[i] < ' '; i--) cad[i] = 0;
}
// Muestra un registro en pantalla, si no est marcado como borrado
void Mostrar(stRegistro &reg)
{
int i;
if(reg.valido == 'S') {
printf("Nombre: %s %s %s\n", reg.nombre, reg.apellido[0], reg.apellido[1]);
printf("Nmero de telfono: %s\n", reg.telefono);
}
system("PAUSE");
}
// Lee el registro desde el fichero de datos con el telfono dado
void Leer(FILE *fa, stRegistro &reg, char *telefono)
Metodologa de la Programacin II

133

Tema 2
{
FILE *fi;
stIndice ind;
long inf, sup, n, nRegs;
fi = fopen("indices.ind", "rb");
fseek(fi, 0, SEEK_END);
nRegs = ftell(fi)/sizeof(stIndice);
// Bsqueda binaria:
inf = 0;
sup = nRegs-1;
do {
n = inf+(sup-inf)/2;
fseek(fi, n*sizeof(stIndice), SEEK_SET);
fread(&ind, sizeof(stIndice), 1, fi);
if(strcmp(ind.telefono, telefono) < 0) inf = n+1;
else sup = n-1;
} while(inf <= sup && strcmp(ind.telefono, telefono));
// Si se encontr el telfono, lee el registro, si no muestra mensaje.
if(!strcmp(ind.telefono, telefono)) {
fseek(fa, ind.indice*sizeof(stRegistro), SEEK_SET);
fread(&reg, sizeof(stRegistro), 1, fa);
}
else {
reg.valido = 'N';
printf("Registro no encontrado\n");
}
fclose(fi);
}
// Aade un registro al archivo de datos y reconstruye los ndices
void Insertar(FILE *fa, stRegistro &reg)
{
// Insertar al final:
fseek(fa, 0, SEEK_END);
fwrite(&reg, sizeof(stRegistro), 1, fa);
ReconstruirIndices(fa);
}
// Lista todos los registros ordenados por el nmero de telfono
void ListarPorTelefonos(FILE *fa)
{
FILE *fi;
stIndice ind;
stRegistro reg;
system("cls");
fi = fopen("indices.ind", "rb");
while(fread(&ind, sizeof(stIndice), 1, fi)) {
134

Dpl. Ing. Carlos Balderrama Vsquez

Almacenamiento Externo de Datos


fseek(fa, ind.indice*sizeof(stRegistro), SEEK_SET);
fread(&reg, sizeof(stRegistro), 1, fa);
printf("%s %s %s %s\n", reg.nombre, reg.apellido[0],
reg.apellido[1], reg.telefono);
}
fclose(fi);
system("PAUSE");
}
// Lista todos los registros del archivo de datos por el orden en que se
// insertaron.
void ListarNatural(FILE *fa)
{
stRegistro reg;
rewind(fa);
system("cls");
while(fread(&reg, sizeof(stRegistro), 1, fa))
printf("%s %s %s %s\n", reg.nombre, reg.apellido[0],
reg.apellido[1], reg.telefono);
system("PAUSE");
}
// Reconstruye el archivo de ndices
void ReconstruirIndices(FILE *fa)
{
long n=0;
FILE *fi;
stRegistro reg;
stIndice ind;
// Crea el fichero de ndices a partir del archivo de datos:
fi = fopen("indices.ind", "w+b");
rewind(fa);
while(fread(&reg, sizeof(stRegistro), 1, fa)) {
strcpy(ind.telefono, reg.telefono);
ind.indice = n++;
fwrite(&ind, sizeof(stIndice), 1, fi);
}
// Ordena usando el algoritmo Quicksort:
QuickSort(fi, 0, n-1);
fclose(fi);
}
// Implementacin del algoritmo Quicksort para fichero de ndices
void QuickSort(FILE *fi, long inicio, long final)
{
long iz, de;
char mitad[10];
Metodologa de la Programacin II

135

Tema 2
static char cad[10];
iz = inicio;
de = final;
strcpy(mitad, LeeCampo(fi, (iz+de)/2, cad));
do {
while(strcmp(LeeCampo(fi, iz, cad), mitad) < 0 && iz < final) iz++;
while(strcmp(mitad, LeeCampo(fi, de, cad)) < 0 && de > inicio) de--;
if(iz < de) Intercambia(fi, iz, de);
if(iz <= de) {
iz++;
de--;
}
} while(iz <= de);
if(inicio < de) QuickSort(fi, inicio, de);
if(iz < final) QuickSort(fi, iz, final);
}
char *LeeCampo(FILE *fi, long n, char *buf)
{
stIndice ind;
fseek(fi, n*sizeof(stIndice), SEEK_SET);
fread(&ind, sizeof(stIndice), 1, fi);
strcpy(buf, ind.telefono);
return buf;
}
void Intercambia(FILE *fi, long iz, long de)
{
stIndice reg1, reg2;
fseek(fi, iz*sizeof(stIndice), SEEK_SET);
fread(&reg1, sizeof(stIndice), 1, fi);
fseek(fi, de*sizeof(stIndice), SEEK_SET);
fread(&reg2, sizeof(stIndice), 1, fi);
fseek(fi, iz*sizeof(stIndice), SEEK_SET);
fwrite(&reg2, sizeof(stIndice), 1, fi);
fseek(fi, de*sizeof(stIndice), SEEK_SET);
fwrite(&reg1, sizeof(stIndice), 1, fi);
}
An no hemos llegado al mayor nivel de optimizacin, nuestro ltimo ejemplo requiere
reconstruir el fichero de ndices cada vez que se aade o se elimina un registro.

136

Dpl. Ing. Carlos Balderrama Vsquez

También podría gustarte