Bitácora del desarrollo de mi clase de C++, en el que publicaré el material de la clase y recibiré comentarios y sugerencias de mis alumnos.

miércoles, 9 de mayo de 2007

10.2 Archivos de Acceso Aleatorio

Archivos de acceso directo o aleatorio
La función fwrite transfiere a un archivo un número especificado de bytes empezando en una posición especificada de memoria. Los datos se escriben al principio de la posición en el archivo indicada mediante el apuntador de posición de un archivo. La función fread transfiere un número especificado de bytes de la posición en el archivo, especificado por el apuntador de posición de archivo, a un área en memoria empezando a partir de una dirección especificada. Por ejemplo, para escribir un entero, en vez de especificar:

fprintf(cuentaPtr, “%d”, numero);
para escribir el mismo número de tipo int, se puede especificar:
fwrite( &numero, sizeof(int), 1, cuentaPtr);
más adelante ese dato puede ser leído con la función fread.

El primer parámetro de fprintf es la dirección de la variable a escribir en disco, el segundo el tamaño de bytes del dato que se va a escribir (devuelto por la función sizeof), el tercer parámetro es el número de datos que se van a escribir del tamaño especificado (por lo común será 1) y el último es el apuntador al archivo que se abrió para escritura o modificación.

Para tener datos de tamaño fijo en los archivos y lo que formalmente serán registros del archivo se crea una estructura, es decir, un nuevo tipo de dato definido por el usuario y compuesto por uno o varios tipos de datos con sus nombres de campo respectivos.

La declaración de un tipo struct se hace antes de la función main de la siguiente manera:

struct nombreEstructura
{
tipo1 campo1;
tipo2 campo2;
tipo3 campo3;
};

Por ejemplo, para crear una estructura que permita almacenar los datos de un cliente puede declararse de la siguiente manera:

struct DatosCliente
{
int cuenta;
char apellido[15];
char nombre[10];
float saldo;
};

De este modo se crea una estructura de nombre DatosCliente con los campos descritos, en el programa se podrán declarar variables de tipo DatosCliente y para acceder a cada uno de los campos se utilizara la notación registro.campo. La declaración de una variable de este tipo se hará:

struct DatosCliente micliente;

De esta manera la variable micliente tiene los cuatro campos definidos para el tipo de estructura, y el nombre del cliente se accede con la siguiente línea:

micliente.nombre

El uso de una estructura y de las funciones fread, fwrite y fseek será básicamente la diferencia entre programar para utilizar un archivo secuencial y un archivo de acceso directo.
Cómo crear un archivo de acceso directo.

/* Creacion de un archivo de acceso directo */
#include (stdio.h)
#include (conio.h)
struct DatosCliente
{
int cuenta;
char apellido[15];
char nombre[10];
float saldo;
};

main( )
{
int i;
struct DatosCliente clienteNulo = {0,"","",0.0};
FILE *cuentaPtr;

if ((cuentaPtr = fopen("cliente.dat","w")) == NULL)
printf("No se puede abrir el archivo\n");
else
{
/* Escritura de 100 registros en “blanco”*/
for (i=1; i<= 100; i++)
fwrite( &clienteNulo, sizeof(struct DatosCliente),1, cuentaPtr);
fclose(cuentaPtr);
}
getch ( );
return 0;
}

Cómo escribir y leer datos de un archivo de acceso directo
La introducción de los datos será mediante la misma función fwrite, donde lo que se escribe es la variable del tipo de la estructura deseada, con el tamaño de la misma estructura y el apuntador al archivo. Cada dato debe ser llenado con scanf al campo deseado de la misma estructura. A continuación se presenta un ejemplo de captura de datos y escritura al archivo.

/* Escribiendo a un archivo de acceso aleatorio */
#include (stdio.h)
#include (stdio.h)
struct DatosCliente
{
int cuenta;
char apellido[15];
char nombre[10];
float saldo;
};

main( )
{
/* la variable cliente de tipo DatosCliente servirá para
asignar los almacenar los valores para cada cliente introducido */
struct DatosCliente cliente;
FILE *cuentaPtr;
if ((cuentaPtr = fopen("cliente.dat","r+")) == NULL)
printf("No se puede abrir el archivo\n");
else
{
printf("Introduzca numero de cuenta"
" (1 a 100), 0 para finalizar)\n?");
scanf("%d", &cliente.cuenta);
while (cliente.cuenta != 0)
{
printf("Introduzca el apellido, nombre y saldo\n? ");
scanf("%s%s%f", cliente.apellido, cliente.nombre, &cliente.saldo);
fseek(cuentaPtr, (cliente.cuenta - 1) *
sizeof(struct DatosCliente), SEEK_SET);
fwrite(&cliente, sizeof(struct DatosCliente), 1, cuentaPtr);
printf("Intruduzca n{umero de cuenta\n? ");
scanf("%d", &cliente.cuenta);
}
}
fclose(cuentaPtr);
getch ( );
return 0;
}

La lectura de cada registro en el archivo se hará con la función fread, cuyos parámetros son: la variable que almacena los datos leídos del archivo, el tamaño de la estructura o del tipo de dato, el número de datos a leer y el apuntador al archivo.

/* Leyendo de un archivo de acceso aleatorio */
#include (stdio.h)
#include (conio.h)
struct DatosCliente
{
int cuenta;
char apellido[15];
char nombre[10];
float saldo;
};
main( )
{
struct DatosCliente cliente;
FILE *cuentaPtr;
clrscr();
if ((cuentaPtr = fopen("cliente.dat","r")) == NULL)
printf("No se puede abrir el archivo\n");
else
{
printf("%-8s%-16s%-11s%10s\n", "Cuenta", "Apellido", "Nombre",
"Saldo");
while (!feof(cuentaPtr))
{
fread(&cliente, sizeof(struct DatosCliente), 1, cuentaPtr);
if (cliente.cuenta != 0)
printf("%-8d%-16s%-1s%10.2f\n", cliente.cuenta,
cliente.apellido, cliente.nombre, cliente.saldo);
}
}
fclose(cuentaPtr);
getch();
return 0;
}

lunes, 7 de mayo de 2007

10.1 Archivos de Acceso Secuencial

Cómo crear un archivo de acceso secuencial
Para abrir un archivo en un programa escrito en C, es necesario utilizar la función fopen(), la cual recibe dos parámetros, el nombre del archivo y el modo de apertura del archivo. Los modos de apertura se describen a continuación.

MODO DESCRIPCIÓN
r Abrir un archivo para lectura.
w Crear un archivo para escritura. Si el archivo ya existe se pierde el contenido actual.
a Agregar, abrir o crear un archivo para escribir al final del mismo.
r+ Abrir un archivo para actualizar (leer y escribir).
w+ Crear un archivo para actualizar, si el archivo ya existe se pierde el contenido actual.
a+ Agregar, abrir o crear un archivo para actualizar, la escritura se efectuará al final del archivo.

A continuación se presenta un ejemplo de creación de un archivo secuencial:

/* Creacion de un archivo de acceso secuencial */
#include (stdio.h)
#include (conio.h)
main( )
{
int cuenta;
char nombre[30];
float saldo;
FILE *cuentaPtr;
if ((cuentaPtr = fopen("cliente.dat","w")) == NULL)
printf("No se puede abrir el archivo\n");
else
{
printf("Introduzca cuenta, nombre y saldo.\n");
printf("Introduzca EOF para terminar.\n");
printf("? ");
scanf("%d%s%f", &cuenta, nombre, &saldo);
while (!feof(stdin))
{
fprintf(cuentaPtr, "%d %s %.2f\n", cuenta, nombre,saldo);
printf("? ");
scanf("%d%s%f", &cuenta, nombre, &saldo);
}
fclose(cuentaPtr);
}
getch ( );
return 0;
}

El enunciado
FILE *cuentaPtr;
establece que cuentaPtr es una apuntador a una estructura de tipo FILE. Cada archivo abierto debe tener un apuntador declarado por separado del tipo FILE, que es utilizado para referirse al archivo.

La línea
if ((cuentaPtr = fopen("cliente.dat","w")) == NULL)
nombra el archivo cliente.dat, para ser utilizado por el programa y establece una comunicación con el archivo a través del apuntador, en este caso el archivo se abre para escritura. La estructura if se utiliza para determinar si el apuntador al archivo cuentaPtr es NULL, es decir, que el archivo no está abierto. Si es NULL, se imprime un mensaje de error y el programa termina, de lo contrario la entrada es procesada y escrita al archivo.

La línea
while (!feof(stdin))
utiliza la función feof para determinar si el indicador de fin de archivo está definido para el archivo a que se refiere stdin. El indicador de fin de archivo le informa al programa que ya no hay más datos a procesar., el indicador de fin de archivo está definido para la entrada estándar cuando el usuario introduce la combinación de teclas de fin de archivo (Control-Z). La estructura while continuará ejecutándose hasta que se defina el indicador de fin de archivo.

El enunciado
fprintf(cuentaPtr, "%d %s %.2f\n", cuenta, nombre,saldo);
escribe datos al archivo cliente.dat, que es al que apunta el apuntador cuentaPtr. Los datos pueden ser recuperados más tarde mediante un programa diseñado para leer el archivo.

Al terminar de ejecutar al programa compruebe la creación del archivo cliente.dat. Desde el explorador de Windows o saliendo al símbolo de sistema mediante la orden File- Dos shell, verifique la existencia y el contenido del archivo cliente.dat. Si se desea que el archivo sea creado en la unidad A:, el parámetro de la función fopen debe ser “a:cliente.dat”, de otra manera se creará en el directorio actual (generalmente C:\TC\BIN).

La función fclose() cierra siempre el archivo al cual apunta el apuntador enviado como parámetro, en este caso fclose(cuentaPtr) cierra el archivo cliente.dat.

Cómo leer datos de un archivo de acceso secuencial
Una vez que se ha creado el arcihvo, la lectura puede resultar más cómoda, pues las diferencias con la creación del archivo son únicamente el modo de apertura del archivo “r”, y la manera en que se leen los datos mediante la función fscanf.

La función fscanf, se le indica de qué archivo se leerán los datos, mandándole como parámetro el apuntador a FILE, en este caso cuentaPtr, la cadena de control para indicarle qué tipo de datos leerá del archivo, y la dirección de las variables a las que asignará los valores leídos (tal como a scanf). Obviamente que si el archivo tiene una ubicación diferente a la actual, el parámetro de nombre de archivo en la función debe tener la ruta completa, por ejemplo
fopen(“A:CLIENTE.DAT, “r”);

A continuación se presenta el ejemplo de lectura del archivo creado en el apartado anterior.

/* Leyendo datos de un archivo de acceso secuencial */
#include (stdio.h)
#include (conio.h)
main( )
{
int cuenta;
char nombre[30];
float saldo;
FILE *cuentaPtr;

if ((cuentaPtr = fopen("cliente.dat","r")) == NULL)
printf("No se puede abrir el archivo\n");
else
{
printf("%-10s%-13s%s\n","Cuenta","Nombre","Saldo");
fscanf(cuentaPtr,"%d%s%f",&cuenta, nombre, &saldo);

while (!feof(cuentaPtr))
{
printf("%-10d%-13s%7.2f\n", cuenta, nombre, saldo);
fscanf(cuentaPtr,"%d%s%f",&cuenta, nombre, &saldo);
}
fclose(cuentaPtr);
}
getch( );
return 0;
}

10.0 Archivos

Procesamiento de archivos
El almacenamiento de datos en variables y en arreglos es temporal; al terminar un programa todos estos datos se pierden. Para la conservación permanente de grandes cantidades de datos se utilizan los archivos. Las computadoras almacenan los archivos en dispositivos de almacenamiento secundario, especialmente en dispositivos de almacenamiento en disco.

La jerarquía de los datos
Todos los elementos de datos procesados por una computadora se reducen a combinaciones de ceros y unos. En una computadora el elemento de datos mas pequeño puede asumir el valor 0 o el valor 1. Este elemento de datos se conoce como un bit.

Sin embargo normalmente los usuarios y programadores de computadoras no trabajan con 0’s y 1’s sino con datos en forma de dígitos decimales, letras y símbolos especiales que se conocen como caracteres, que pueden ser representados por conjuntos de 8 bits, es decir, un byte, que no es otra cosa más que diferentes combinaciones de 1 y 0 en esos 8 bits.

Al igual que los caracteres están formados por bits, los campos están formados por caracteres. Un campo es un grupo de caracteres que contiene un significado. Por ejemplo, un campo que consista únicamente de letras mayúsculas y minúsculas, puede ser utilizado para representar el nombre de una persona.

Un registro es un conjunto de campos relacionados, y que generalmente son atributos de una misma entidad, por ejemplo, un registro de alumnos, estará formado por los campos de Nombre, Edad, Semestre, Grupo, etc., todos ellos datos del propio alumno.

Un archivo es un conjunto de registros del mismo tipo, siguiendo con el ejemplo anterior, el archivo para los alumnos de este grupo, tendría 32 registros, un registro para almacenar los datos de cada alumno.

Existen varias formas de organizar los registros dentro de un archivo. El tipo más popular de organización se conoce como archivo secuencial o archivo de acceso secuencial, esto significa que los registro se leen y se escriben al archivo uno tras otro.

Archivos y flujos
C ve cada uno de los archivos como un flujo secuencial de bytes. Cada archivo debe terminar con un marcador de fin de archivo.

Siempre que se ejecuta un programa se abren tres archivos y sus flujos asociados: la entrada estándar, la salida estándar y el error estándar. Cada uno de ellos es manejado con un apuntador de archivo, stdin, stdout y stderr, respectivamente.