| Artículos | 01 NOV 1995

Accesos a archivos por corrientes en C++

Tags: Histórico
Formateo de cadenas de caracteres
Jaime Peña.

A lo largo del presente artículo, comentaremos detenidamente las principales cuestiones relativas al manejo de archivos en C++.

La gestión de entradas y salidas sobre archivos es uno de los aspectos en los que el lenguaje C++ muestra su mayor potencial. A la par de eso, que será la parte fundamental del presente artículo, veremos otro aspecto importante relacionado con las entradas y salidas: el formateo en memoria de cadenas de caracteres.

Trabajando conjuntamente con ambas posibilidades, veremos cómo adaptar nuestros programas al nuevo estilo de programación que la OOP nos propone. Las ventajas logradas lo son a tres niveles: mayor potencia de las funciones y operadores disponibles; mayor claridad del código; y una más fácil adaptación de programas, a la hora de programar código, para el trabajo conjunto sobre pantalla y archivos.

Opciones y orientaciones prácticas

Los accesos a archivos (para lectura y/o escritura) son algo especialmente interesante del trabajo con corrientes. Gran parte del atractivo de ellas se debe a la similitud, y aun de la igualdad, del trabajo con corrientes estándar (sobre teclado y pantalla) y del trabajo con corrientes de archivos. Podremos, por ejemplo, hacer programación inespecífica que, posteriormente, redireccionaremos a la corriente que el usuario elija.

En esencia, el trabajo sobre un archivo conlleva las siguientes tareas:

* Declaración de un objeto del tipo adecuado a la(s) operación(es) que deseemos realizar; por ejemplo, una lectura de datos implica la declaración de un objeto tipo ifstream; operaciones de escritura nos fuerzan a seleccionar objetos del tipo ofstream y, finalmente, operaciones mixtas de lecturas y escritura, nos forzarían a declarar objetos del tipo fstream, que heredan las propiedades de las clases ifstream y ofstream.

* Deberemos emplear una función miembro de apertura de la corriente, típicamente la función open; aunque también es posible asociar archivos en los constructores de las clases, bien pasándoles un nombre de ruta completo, o asociándolo con un descriptor de archivo previamente abierto (una estructura de tipo FILE, que es la empleada en las funciones de lenguaje C de entrada/salida estándar ó de alto nivel; ejemplos son fopen, freopen, tmpfile, etc.)

* Como en todo proceso bien programado, la detección de posibles errores debe ser un paso natural; en el caso de usarse la función miembro open, deberemos comprobar que no se devuelve un valor nulo (NULL). En tal caso, podremos determinar qué ha ocurrido y, o bien, tomar las medidas de corrección oportunas, o bien informar al usuario del suceso, para que pueda tomar algún tipo de decisión.

* El siguiente paso, si toda ha ido correctamente, son las operaciones de lectura y/o escritura sobre la corriente del archivo.

* Finalmente, deberemos proceder, lo antes posible, al cierre de la corriente. Con ello garantizamos el vaciado del buffer de datos y que la información intercambiada no se pierda por salidas incontroladas. No es buena costumbre dejar corrientes abiertas a la espera de una salida final de la aplicación. Es un riesgo que no conviene correr.

Veamos, paso a paso, cada uno de estos aspectos. Para comenzar, supongamos que se quiere proceder a la apertura de un archivo para lectura de datos, fijémonos en el listado de la figura 1. Simple, pero ilustrativo, nos pone sobre la pista de todo lo anteriormente comentado.

La declaración de la clase asocia el objeto entrada a una corriente tipo ifstream; no precisamos más, aunque podríamos haberla declarado del tipo más genérico fstream. El constructor de la clase que hemos empleado no asocia, en principio, ningún archivo concreto a la corriente. Una forma alternativa sería la declaración ifstream entrada(fNombre); y comprobar que entrada no es nulo (if (!entrada) ...) inmediatamente después. Con ello nos ahorraríamos la llamada a la función miembro open.

Sin embargo, el empleo de open conlleva algunas ventajas: clarificación del código y no asociar un archivo fijo a la corriente. Imaginemos que se emplee una corriente para lectura de diferentes archivos, no nos convendrá asociar sólo uno en la declaración del objeto.

La función miembro open tiene una declaración que toma tres posibles parámetros: el nombre completo de la ruta del archivo, un valor entero que define el modo de apertura y, finalmente, un valor entero que define el modo de protección de accesos. Este último no es usado en la programación bajo DOS; podremos ignorarlo, dado que se empleará el valor por defecto para DOS.

Los modos de apertura se recogen en la figura 2. Los correspondientes a abrir para lectura, ios::in, y abrir para escritura, ios::out, son los implícitos en las clases ifstream y ofstream, respectivamente. Otras posibilidades son:

* Apertura para adicionar al final, modo ios::app. Se puede especificar en accesos para escritura. Con ello, todas las escrituras sobre el archivos serán, en principio, realizadas al final (adición). Naturalmente, posicionamientos posteriores podrían llevarnos a otros modos de escritura.

* Apertura truncando el archivo, modo ios::trunc. Es útil en accesos para escritura que deseen eliminar el contenido anterior del archivo y escribir en su inicio. Este modo está implícito cuándo se realiza una apertura para escritura (bien sea como un objeto de la clase ofstream ó fstream) y no se especifica que se posicione el puntero al final (modos ios::ate ó ios::app).

* Por defecto, todas las corrientes se abren en modo texto. Ello implica que las combinaciones de caracteres ASCII retorno de carro y nueva línea se convertirán, en la lectura, en un carácter '\n'. En el proceso de escritura sucederá, justamente, lo contrario. No siempre nos conviene eso, por ello se dispone del modo ios::binary, que transcribe exactamente los caracteres leídos y/o escritos, sin ningún tipo de conversión.

* Por último, el sistema de corrientes de C++ nos permite un control sencillo de si se debe crear un archivo nuevo o si debe ser reemplazado. Los modos ios::nocreate e ios::noreplace fuerzan un error de apertura si el archivo no existe o, si existiendo, se trata de truncar su contenido, respectivamente. Como vemos, un modo muy sencillo de protección ante acciones irreversibles sobre archivos.

Control de la apertura

La función miembro open devuelve un valor de corriente, si es nula algo extraño habrá ocurrido. Para saber qué es exactamente lo que falla se puede usar la función miembro rdstate, que no toma parámetros. Después de ser llamada devuelve un valor entero. Sus posibles valores se recogen en el miembro enumeración io_state (véase la figura 3).

El modo normal de trabajo implicará que, inmediatamente después de una operación que pueda ser crítica (como es el caso del intento de apertura de una corriente de archivo), comprobemos el estado de la corriente. En el ejemplo que tratamos, y en general siempre será así, se emplea una estructura "switch(corriente.rdstate()) ..." para identificar las posibles causas de error. Obsérvese el listado de la figura 1, en el que se determina si la corriente es nula y, en tal caso, se averiguará por qué.

Además de la función miembro rdstate(), hay otras que nos pueden revelar, simplemente, si hubo o no errores. Por ejemplo, la función miembro bad() devolverá un valor distinto de cero si hubo error en alguna operación; la función miembro fail() se comportará igual si falló algo; y, finalmente, la función miembro good() devolverá un valor no cero si no hay bits de estado fijados (no habrá habido errores detectados). Todas ellas son modos alternativos de tratar estados de errores en accesos a corrientes en general: sea el tipo de corriente que sea y fuera la operación realizada que fuera.

Redireccionamientos, lecturas y escrituras

El trabajo

Contenidos recomendados...

Comentar
Para comentar, es necesario iniciar sesión
Se muestran 0 comentarios
X

Uso de cookies

Esta web utiliza cookies técnicas, de personalización y análisis, propias y de terceros, para facilitarle la navegación de forma anónima y analizar estadísticas del uso de la web. Consideramos que si continúa navegando, acepta su uso. Obtener más información