En C, el proceso de abrir y manipular ficheros requería un gran conocimiento del lenguaje para prepararle para la complejidad de las operaciones. Sin embargo, la librería iostream de C++ proporciona una forma simple de manejar ficheros, y por eso se puede presentar mucho antes de lo que se haría en C.
Para poder abrir un fichero para leer y escribir, debe incluir la librería fstream. Aunque eso implica la inclusión automática de la librería iostream, es prudente incluir explícitamente iostream si planea usar cin, cout, etc.
Para abrir un fichero para lectura, debe crear un objeto ifstream que se comporta como cin. Para abrir un fichero para escritura, se crea un objeto ofstream que se comporta como cout. Una vez que tiene abierto el fichero puede leer o escribir en él como si usara cualquier objeto iostream. Así de simple, que es el objetivo, por supuesto.
Una de las funciones más útiles de la librería iostream es getline(), que permite leer una línea (terminada en nueva línea) y guardarla en un objeto string [39]. El primer argumento es el objeto ifstream del que se va a leer la información y el segundo argumento es el objeto string. Cuando termina la llamada a la función, el objeto string contiene la línea capturada.
Aquí hay un ejemplo sencillo que copia el contenido de un fichero en otro:
//: C02:Scopy.cpp // Copy one file to another, a line at a time #include <string> #include <fstream> using namespace std; int main() { ifstream in("Scopy.cpp"); // Open for reading ofstream out("Scopy2.cpp"); // Open for writing string s; while(getline(in, s)) // Discards newline char out << s << "\n"; // ... must add it back } ///:~
Listado 2.8. C02/Scopy.cpp
Para abrir los ficheros, únicamente debe pasar a los objetos ifstream y ofstream los nombres de los ficheros que quiere crear, como se ve arriba.
Aquí se presenta un nuevo concepto: el bucle while. Aunque será explicado en detalle en el siguiente capítulo, la idea básica consiste en que la expresión entre paréntesis que sigue al while controla la ejecución de la sentencia siguiente (pueden ser múltiples sentencias encerradas entre llaves). Mientras la expresión entre paréntesis (en este caso getline(in, s)) produzca un resultado «verdadero», las sentencias controladas por el while se seguirán ejecutando. getline() devuelve un valor que se puede interpretar como «verdadero» si se ha leído otra línea de forma satisfactoria, y «falso» cuando se llega al final de la entrada. Eso implica que el while anterior lee todas las líneas del fichero de entrada y las envía al fichero de salida.
getline() lee los caracteres de cada línea hasta que descubre un salto de línea (el carácter de terminación se puede cambiar pero eso no se verá hasta el capítulo sobre iostreams del Volumen 2). Sin embargo, descarta el carácter de nueva línea y no lo almacena en el objeto string. Por tanto, si se quiere que el fichero copiado sea idéntico al original, hay que añadir el carácter de nueva línea como se muestra arriba.
Otro ejemplo interesante es copiar el fichero entero en un único objeto string:
//: C02:FillString.cpp // Read an entire file into a single string #include <string> #include <iostream> #include <fstream> using namespace std; int main() { ifstream in("FillString.cpp"); string s, line; while(getline(in, line)) s += line + "\n"; cout << s; } ///:~
Listado 2.9. C02/FillString.cpp
Debido a la naturaleza dinámica de los strings, no hay que preocuparse de la cantidad de memoria que hay que reservar para el string. Simplemente hay que añadir cosas y el string irá expandiéndose para dar cabida a lo que le introduzca.
Una de las cosas agradables de poner el fichero entero en una cadena es que la clase string proporciona muchas funciones para la búsqueda y manipulación que le permiten modificar el fichero como una única cadena. Sin embargo, tiene sus limitaciones. Por un lado, a menudo, es conveniente tratar un fichero como una colección de líneas en vez de un gran bloque de texto. Por ejemplo, si quiere añadir numeración de líneas es mucho más fácil si tiene un objeto string distinto para cada línea. Para realizarlo, hace falta otro enfoque.
[39] En realidad existen varias variantes de getline(), que se discutirán profusamente en el capítulo de iostreams en el Volumen 2.