Ejemplos java y C/linux

Tutoriales

Enlaces

Licencia

Creative Commons License
Esta obra está bajo una licencia de Creative Commons.
Para reconocer la autoría debes poner el enlace http://www.chuidiang.org

Ficheros de texto en C++

Antes de nada, pedir disculpas por este tutorial porque es tan tonto que me da vergüenza ponerlo, pero ahí va, por si a alguien le sirve.

Ya hemos visto cómo leer y escribir ficheros de texto desde C. Eso vale perfectamente para C++, pero C++ tiene unas clases específicas para estas tareas: ifstream, ofstream y fstream. Vamos a ver aquí un ejemplo básico de cómo usarlas.

ifstream, ofstream y fstream

Las clases que nos da C++ para el acceso a ficheros de texto son ifstream, ofstream y fstream. La primera es para leer del fichero ( i de input, f de file y stream). La segunda es para escribir (o de output, f de file y stream). La tercera vale para las dos cosas, lectura y escritura.

Vamos a hacer un pequeño programa de copia de un fichero de texto. Lo primero, abrir los ficheros, el fichero original para leer de él y un fichero nuevo para escribir en él.

Abrir los ficheros

La apertura del fichero se puede hacer pasando parámetros al declarar la variable de estos tipos, o bien declarando la variable sin parámetros y luego llamando al método open(). Vamos a abrir cada uno de una manera distinta, para que veas ambas

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
   /* Fichero original, se abre para lectura pasando parámetros en la declaración de la variable */

   ifstream f("fichero.txt", ifstream::in);

   /* Fichero nuevo para copiar, se abre después de declararlo, llamando a open() */
   ofstream f2;
   f2.open("fichero2.txt", ofstream::out);

El primer parámetro es el nombre del fichero. Como siempre, path absoluto o relativo según nos interese. Para tus pruebas, sobre todo si usas algún IDE, te aconsejo path absoluto.

El segundo parámetros son unos flags para indicar cómo queremos abrir el fichero. Estos flags están en la clase ios_base::openmode y pueden ser los siguientes

Si queremos abrir el fichero con varias de estas opciones, tenemos que componerlas con un OR (el caracter | ), tal que así

f2.open("fichero2.txt", ofstream::out | ofstream::trunc);

Como estos flags son de una clase padre común, podemos acceder a ellos desde cualquier clase hija, por eso podemos poner ofstream::out o ifstream::in, e incluso ifstream::out aunque parezca que no tiene sentido.

Hay varias formas de comprobar si ha habido o no un error en la apertura del fichero. La más cómoda es usar el operator ! que tienen definidas estas clases. Sería de esta manera

if (!f)
{
   cout << "fallo" << endl;
   return -1;
}

Ese !f (no f) tan raro es el que nos dice si ha habido o no un error. !f devuelve true si ha habido algún problema de apertura del fichero.

Leer y escribir en el fichero

También tenemos muchos metodos específicos para leer y escribir bytes o texto: get(), getline(), read(), put(), write(), etc.

Sin embargo, los más curiosos son los operadores << para escribir y >> para leer.

/* Declaramos un array con suficiente tamaño para leer las líneas */
char cadena[100];
...
/* Leemos */
f >> cadena;
...
/* y escribimos */
f2 << cadena;

Como pretendemos copiar un fichero, tendremos que meter esto en un bucle, leyendo una línea de un fichero y escribiéndola en otro, leyendo de uno y escribiendo en otro ... hasta que detectemos el final del fichero de entrada.

Para detectar el final del fichero de entrada tendemos el método feof(), que nos devuelve true si hemos inentado leer después del fin de fichero. He puesto después con negrita porque feof() NO devuelve true cuando estamos al final del fichero, sino cuando intentamos sobrepasarlo.

Por ello, no podemos hacer este bucle

/* Este bucle está mal */
while (!f.feof())
{
   f >> cadena;
   f2 << cadena;
}

Si el fichero está vacío, como no hemos hecho todavía ninguna lectura, feof() dirá que todavía no es el fin de fichero. Haremos una lectura que fallará (no hay nada que leer) y escribiremos "algo" en f2, lo que tuviera cadena antes de leer (puesto que no hemos leido nada).

Si el fichero no está vacío, todo funcionará aparentemente correcto, pero cuando lleguemos y hayamos escrito la última línea del fichero, feof() todavía dirá que no es el fin de fichero, así que será una nueva lectura, fallida. Escribiremos cadena otra vez con el contenido anterior. El resultado es que el fichero de salida contendrá dos veces la última línea.

Un posible bucle correcto es este

/* Hacemos una primera lectura */
f >> cadena;
while (!f.feof())
{
   /* Escribimos el resultado */
   f2 << cadena << endl;
   /* Leemos la siguiente línea */
   f >> cadena;
}

La primera lectura nos sirve para asegurarnos que feof() indica final de fichero y no entrar en el bucle si el fichero está vacío. Nos permite además cambiar el orden dentro del bucle, primero escribimos el dato que ya tenemos y luego intentamos leer la siguiente línea. Si no la hubiera, terminaría el bucle.

El endl que hemos puesto al final de la escritura es un fin de línea. Cuando leemos con f >> cadena, se lee hasta el final de línea, pero este no se incluye en cadena, sino que se descarta. Por ello, cuando escribmos, después de escribir cadena, necesitamos escribir el fin de línea.

Cerrar los ficheros

Ahora queda lo más sencillo de todo, cerrar los ficheros.

f.close();
f2.close();

Estadísticas y comentarios

Numero de visitas desde el 4 Feb 2007: