5.3.3 E/S asociadas con ficheros
§1 Presentación
En el capítulo 5.3.2 se incluyó una descripción pormenorizada de la estructura y funcionalidades de la Librería Estándar C++ de E/S, de forma que, básicamente, ya han quedado descritas las funcionalidades que puede ofrecer esta jerarquía. No obstante, por ser con mucho las más frecuentes, abordaremos en este capítulo el manejo de las E/S desde la óptica de los denominados filestreams; encargados de las E/S a ficheros. Nos centraremos en los aspectos más usuales mediante ejemplos concretos, dejando los detalles a lo ya indicado ( 5.3.2) y al manual que acompaña a su compilador. La exposición la dividimos en dos secciones, que corresponden respectivamente a las rutinas para E/S formateadas y no formateadas. A estas últimas las denominamos también como E/S "planas".
Nota: las clases filestreams no solo permiten manejar E/S a ficheros de disco, que son los más usuales. En realidad permiten manejar flujos relacionados con cualquier fichero (dispositivo) asociado a un nombre o descriptor equivalente ("Named files" & "Named devices") [1].
§2 Sinopsis
Como se ha indicado ( 5.3.1), la Librería Estándar de Plantillas C++ (STL) dispone de tres clases genéricas para manejar E/S a ficheros. Precisamente esta circunstancia hace que dispongan de métodos específicos para asociar un fichero al flujo y controlar el estado de esta asociación:
basic_ifstream Con funciones para leer e interpretar el flujo de entrada (lecturas).
basic_ofstream Ayudan a formatear y escribir el flujo de salida (escritura)
basic_fstream Dispone de funciones para leer y escribir flujos.
Estas plantillas pueden especializarse para manejar flujos de caracteres de cualquier tipo, aunque los más frecuentes son los char y wchar_t, para los que existen incluso typedefs adecuados ( 5.3.2). En ambos casos el tratamiento es análogo, por lo que nos centraremos en los primeros (flujos de tipos char), que manejaremos mediante los typedefs correspondientes. En consecuencia, recuerde que las siguientes expresiones como equivalentes:
basic_ifstream<char> objIn; // crear objeto Input
ifstream objIn; // Idem.
basic_ofstream<char> objOut; // crear objeto Output
ofstream objOut; // Idem.
basic_fstream<char> objInOut; // crear objeto Input/Output
fstream objInOut; // Idem.
Las clases anteriores derivan respectivamente de basic_istream, basic_ostream y
basic_iostream, (
5.3.2), por lo que pueden manejar
flujos formateados y sin formato. Por supuesto, para utilizar cualquiera de ellas es necesario incluir en el fuente la
cabecera correspondiente (
5.3.1), en nuestro caso <fstream>.
No olvide que los ifstreams disponen de métodos que son propios y exclusivos de lectura (entradas) y los ofstreams para escritura (salidas). Por consiguiente, los objetos creados exclusivamente para lectura no pueden utilizar los métodos de escritura (no disponen de ellos) y viceversa. Los únicos que disponen de toda la panoplia de recursos para entradas y salidas, son los fstreams.
§2.1 Almacenamiento temporal
Para una cabal comprensión de los mecanismos involucrados, es preciso recordar aquí lo indicado respecto a su arquitectura interna ( 5.3). La instanciación de estos objetos supone la creación de un objeto asociado tipo basic_filebuf, que representa el almacenamiento temporal. Precisamente el control del flujo (transporte) es realizado por la capa que relaciona el buffer con el fichero, lo que significa que en última instancia son los métodos del filebuf los que controlan estas operaciones. Por ejemplo, el método seekg() ejecuta la operación mediante una invocación al método pubseekpos( 5.3.2f) del referido buffer. Sin embargo, como tales métodos son invocados de forma automática por los métodos de los filestreams, estos detalles no suele tener consecuencias prácticas para el programador (aunque en ocasiones no esté de más conocerlos).
§2.2 Excepciones
Durante las operaciones de E/S, en caso de fallo o error, los filestreams realizan una invocación setstate(failbit). Este método puede a su vez lanzar una excepción de tipo ios_base::failure. Por esta razón, es aconsejable englobar las operaciones con filestreams en bloques try - catch y utilizar el mecanismo de excepciones ( 1.6) para controlar las incidencias.
Ejemplo:
try {
ifstream objFile;
objFile.open("somefile.txt");
...
...
} catch( ios_base::failure& var ) {
cout << var.what() << endl;
}
...
[1] Win32 utiliza el prefijo "\\.\'' para identificar los "named devices". Ejemplo: "\\\\.\\Lucent Win Modem"