12. Ficheros en C++
Curso: Introducción a C++, por Nacho Cabanes (antiguo)
12. Ficheros en C++
Al igual que ocurre con la escritura en pantalla, a la hora de manejar los ficheros desde C++, podemos emplear las funciones que ya conocíamos de C, o bien emplear otras nuevas posibilidades que aporta C++. También al igual que ocurría con la pantalla, el manejo de ficheros se basará en flujos de entrada y salida.
Tenemos las clases fstream (fichero, en general), ifstream (fichero de entrada) y ofstream (fichero de salida), todas ellas definidas en "fstream.h". Leeremos y escribiremos con << y >>, al igual que para la pantalla. Cerraremos un fichero con "close" (tanto si lo hemos abierto como para leer o para escribir) y comprobaremos si se ha terminado un fichero de entrada con "eof".
Vamos a ver un primer ejemplo que lo aplique, creando un fichero de texto:
1: //
2: // fich01.cpp
3: //
4: // Introduccion a C++
5: // Ejemplo de ficheros (escritura)
6: // Probado con BC++ 5.5 FCLT
7: //
8: // Curso de C
9: // Jose Ignacio Cabanes
10: //
11: /////////////////////////////////////////////
12: #include <fstream.h>
13: main()
14: {
15: ofstream fichero("ejemplo.txt");
16: fichero << "Hola" << endl;
17: fichero << "Adios" << endl;
18: fichero.close();
19: }
Debería ser muy fácil de seguir:
- Incluimos el fichero de cabecera "fstream.h".
- Definimos un fichero de salida, que tendrá por nombre físico "ejemplo.txt"
- Escribimos dos líneas de texto en el fichero.
- Cerramos en el fichero.
En un caso general, puede ocurrir que no sepamos el nombre físico del fichero en el momento de definir la variable "fichero", sino más tarde (por ejemplo, porque el usuario sea el que vaya a teclear el nombre del fichero con el que trabajar, o porque vaya a escoger dicho nombre en una ventana de diálogo). En ese caso, podemos usar la función miembro "open". Como ejemplo, vamos a leer el fichero que acabamos de crear:
1: //
2: // fich02.cpp
3: //
4: // Introduccion a C++
5: // Ejemplo de ficheros (lectura)
6: // Probado con BC++ 5.5 FCLT
7: //
8: // Curso de C
9: // Jose Ignacio Cabanes
10: //
11: /////////////////////////////////////////////
12: #include <fstream.h>
13: #include <iostream.h>
14:
15: main()
16: {
17: fstream fichero;
18: char texto[200];
19: // Abro para lectura
20: fichero.open("ejemplo.txt", ios::in);
21: fichero >> texto; // Leo una primera linea
22: while (!fichero.eof()) // Mientras se haya podido leer algo
23: {
24: cout << texto << endl; // Muestro lo que lei
25: fichero >> texto; // Y vuelvo a intentar leer
26: }
27: fichero.close(); // Finalmente, cierro
28: }
La estructura es ligeramente distinta, pero aun así, debería resultar fácil de seguir. Esta vez, el fichero lo hemos declarado como "genérico", sin especificar si va a ser para lectura o escritura, de modo que este dato lo indicamos cuando realmente abrimos el fichero. Los modos de apertura que tenemos disponibles son:
- ios::in abre el fichero para lectura
- ios::out abre el fichero para escritura
- ios::append abre el fichero para añadir datos (al final, después de los que ya contenga)
Si hubiéramos declarado el fichero como "ifstream", se daría por sentado que lo abrimos para leer, y no sería necesario indicarlo:
ifstream fichero;
// Abro para lectura
fichero.open("ejemplo.txt");
No hemos comprobado si el fichero realmente se ha podido abrir. Para conseguirlo, añadiríamos después de "open" algo parecido a esto, similar a lo que hacíamos en C estándar:
if (!fichero)
{
cerr << "No se ha podido abrir el fichero." << endl;
exit(1);
}
Estas son las ideas básicas. Pero hay más posibilidades, que voy a comentar con menos detalle, pero prefiero que se sepa que existen, porque pueden ser útiles en muchos casos. Por ejemplo, podemos leer un bloque de datos de una determinada longitud, lo que será útil cuando manejemos ficheros binarios, o escribir una serie de bytes, o leer un dato de un flujo pero sin avanzar de posición.
Estas son algunas funciones miembro de iostream que nos servirán para cosas como esas:
- put(char c) escribe un carácter en un flujo de salida
- get(char& c) lee un carácter de un flujo de entrada.
- write(const char* s, int n) escribe n bytes de la cadena s en un flujo de salida (normalmente se usará para salida binaria).
- read(char* s, int n) lee n bytes del flujo de entrada y los deposita en la cadena s (normalmente se usará para entrada binaria).
- get(char* s, int n, char c="™\n"™) lee como máximo n caracteres del flujo de entrada (incluyendo el "˜\0"™) y los introduce en la cadena s, o hasta que encuentre el carácter de terminación (por defecto "˜\n"™, salto de línea), o el fin de fichero. No retira el carácter de terminación del flujo de entrada.
- getline(char* s, int n, char c="™\n"™) lee como máximo n-1 caracteres del flujo de entrada, o hasta que encuentre el carácter de terminación (por defecto un final de línea) o hasta el fin de fichero. Retira el carácter de terminación del flujo de entrada, pero no lo almacena en la cadena s.
- ignore(int n=1, int delim=EOF) ignora o descarta los n caracteres siguientes de un flujo de entrada (o un solo carácter, si no se indica el valor de n), o hasta que encuentra un cierto carácter de terminación (por defecto el fin de fichero EOF).
- peek() lee un carácter del flujo de entrada pero sin retirarlo de dicho flujo.
- putback(char c) devuelve el carácter c al flujo de entrada (de modo que sería lo primero que se leería en la próxima operación de entrada).
Por otra parte, en la clase "fstream" tenemos otras funciones miembro que nos ayudarán a comprobar errores en la lectura o escritura:
- good () devuelve un valor distinto de cero si no ha habido ningún error.
- eof() devuelve un valor distinto de cero si se ha llegado al fin del fichero, como ya hemos visto.
- bad() devuelve un valor distinto de cero si ha habido un error grave de entrada/salida grave. No se puede continuar en esas condiciones.
- fail() devuelve un valor distinto de cero si ha habido cualquier error de E/S distinto de EOF. Después podemos llamar a bad() para comprobar si el error es grave o si se puede intentar proseguir la lectura. Si bad() devuelve 0, el error no es grave y la lectura puede proseguir después de llamar a la función clear().
- clear() resetea la situación de error (siempre que no sea grave), para poder seguir leyendo.
if (!fichero)
{
// No se ha podido abrir el fichero
}
o como
if
(!fichero.get(ch)) {
// No se ha podido leer el siguiente dato.
}
Actualizado el: 09-07-2006 13:42