ficheros en c++

26
___________________________________________________________________________ Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 1 9 GESTIÓN BÁSICA DE FICHEROS Contenido ___________________________________________________________________________ 9.1.- Introducción . 9.2.- Archivos C++. 9.2.1.- Apertura de ficheros. 9.2.2.- Cierre de ficheros. 9.2.3.- Detección de fin de fichero y otras funciones. 9.2.4.- Comprobación de apertura correcta. 9.3.- Lectura/Escritura en ficheros de texto. 9.3.1.- Avance del cursor. 9.3.2.- Ficheros de texto. 9.4.- Ficheros binarios. 9.4.1.- Utilidad de los ficheros binarios. 9.4.2.- Lectura/Escritura byte a byte. 9.4.3.- Lectura/Escritura por bloques de bytes. 9.5.- Acceso aleatorio a ficheros. 9.6.- Ficheros pasados como argumentos. 9.7.- Ejemplos de utilización de ficheros binarios y de texto. Ejercicios. ___________________________________________________________________________ En este capítulo vamos a tratar la gestión básica de ficheros (también llamados archivos) en C++ y lo haremos a partir del concepto de flujo. Vamos a estudiar cómo declarar los identificadores que establecen el modo de flujos de los ficheros (de entrada o salida básicamente) y procuraremos no entrar en el concepto de clase de manera que la gestión de ficheros resulte asimilable por el alumno con los conceptos aprendidos hasta el momento.

description

c++

Transcript of ficheros en c++

Page 1: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 1

9 GESTIÓN BÁSICADEFICHEROS

Contenido___________________________________________________________________________

9.1.- Introducción .

9.2.- Archivos C++.

9.2.1.- Apertura de ficheros.

9.2.2.- Cierre de ficheros.

9.2.3.- Detección de fin de fichero y otras funciones.

9.2.4.- Comprobación de apertura correcta.

9.3.- Lectura/Escritura en ficheros de texto.

9.3.1.- Avance del cursor.

9.3.2.- Ficheros de texto.

9.4.- Ficheros binarios.

9.4.1.- Utilidad de los ficheros binarios.

9.4.2.- Lectura/Escritura byte a byte.

9.4.3.- Lectura/Escritura por bloques de bytes.

9.5.- Acceso aleatorio a ficheros.

9.6.- Ficheros pasados como argumentos.

9.7.- Ejemplos de utilización de ficheros binarios y de texto.

Ejercicios.

___________________________________________________________________________

En este capítulo vamos a tratar la gestión básica de ficheros (también llamados archivos) en C++y lo haremos a partir del concepto de flujo.

Vamos a estudiar cómo declarar los identificadores que establecen el modo de flujos de losficheros (de entrada o salida básicamente) y procuraremos no entrar en el concepto de clase demanera que la gestión de ficheros resulte asimilable por el alumno con los conceptos aprendidoshasta el momento.

Page 2: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 2

9.1.- INTRODUCCION.

En este tema se verán las características básicas que nos permitirán trabajar con ficheros

(también llamados archivos).

• ¿ Por qué se necesitan los ficheros?

Todos los programas vistos hasta ahora trabajaban con información almacenada en

memoria principal, no obstante, hay situaciones en que esto no es apropiado. Algunas de esas

situaciones podrían ser:

* Los datos con los que necesita trabajar el programa son demasiado grandes (ocupanmucha memoria) para que entren en la memoria principal.

* Nos interesa mantener la información después de cada ejecución, necesitamosutilizar datos procedentes de otros programas (editores, etc.), o generar datos paraque puedan ser utilizados por otros programas.

En dichos casos se utilizarán ficheros para contener la información en memoria

secundaria (disco duro, disquetes, etc.).

• Definición de Fichero:

Es una colección de elementos lógicamente relacionados y almacenados en memoria

secundaria. A más bajo nivel, un fichero es una secuencia de bits almacenado en algún

dispositivo externo (como por ejemplo uno de memoria secundaria).

9.2.- ARCHIVOS EN C++.

En los temas anteriores hemos visto que los datos de entrada o salida en un programa

C++ son obtenidos o enviados directamente a través de lo que llamamos flujos de entrada o

salida. Hasta hora hemos manejado los flujos estándares cin y cout, y una serie de operadores

(<< y >>) y funciones (como get o getline por ejemplo) disponibles a partir de la biblioteca

iostream y que son útiles para la entrada/salida de la información.

En C++ un fichero es simplemente un flujo externo que se puede abrir para entrada

(dando lugar a un flujo de archivo de entrada que, para simplificar, llamaremos simplemente

archivo o fichero de entrada), para salida (dando lugar a un flujo de archivo de salida que,

para simplificar, llamaremos simplemente archivo o fichero de salida) o para entrada-salida

(archivo o fichero de entrada-salida o archivo de E/S).

C++ soporta dos tipos de archivos: de texto y binarios. Los primeros almacenan datos

como códigos ASCII. Los valores simples, tales como números y caracteres están separados

Page 3: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 3

por espacios. Los segundos almacenan bits de forma directa y se necesita usar la dirección de

una posición de almacenamiento.

Una biblioteca en C++ que proporciona “funciones” y operadores para el manejo de

ficheros es la biblioteca fstream. En general emplearemos ésta para la gestión básica de

ficheros por lo que deberemos incluir el archivo de cabecera correspondiente en nuestros

programas mediante la declaración adecuada, o sea, incluyendo la directiva de pre-

procesamiento siguiente

#include <fstream.h>

La biblioteca fstream ofrece un conjunto de funciones y operadores comunes a todas las

operaciones de Entrada/Salida (E/S) de ficheros.

9.2.1.- Apertura de ficheros.

Antes de que un programa pueda manipular un fichero para leer o escribir información se

debe abrir (o crear si es necesario) el fichero para identificar la posición del mismo en el

programa (o sea, la dirección de memoria a partir de la cual almacenaremos o leeremos el

contenido del fichero). Ya hemos indicado anteriormente que los archivos son tratados como

flujos de entrada/salida por lo que, al igual que sucede con los flujos estándares y por defecto

cin y cout, la información “debe” ser transferida en una sola dirección por lo que, salvo en

ciertos casos, los ficheros deben abrirse bien para entrada o bien para salida.

9.2.1.1.- Ficheros de entrada o salida.

Supongamos que tenemos un fichero cuyo nombre en el sistema operativo es

“nombre.extensión”. Entonces hay varias formas de abrirlo.

(A) Como fichero de entrada: Para ello empleamos la siguiente declaración

ifstream descriptor (“nombre.extensión”);

(B) Como fichero de salida: Para ello empleamos la sentencia

ofstream descriptor (“nombre.extensión”);

Page 4: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 4

En ambos casos descriptor es una variable que se asocia al fichero cuyo nombre es

“nombre.extensión”.

Comentario:

Observe que a partir de la apertura de un fichero, el descriptor del fichero será utilizado en

todas las operaciones que se realicen sobre el fichero (OJO! no utilizamos el nombre

original del fichero).

Otra forma de abrir un fichero para operar sobre él consiste en crear primero el flujo de

entrada asociado al mismo, o sea, en asociar una variable descriptor a un fichero en una

forma similar a como se declara una variable en C++, es decir, mediante una sentencia tal

como

ifstream descriptor; // Para ficheros de entrada

ofstream descriptor; // Para ficheros de salida

y, posteriormente, abrir el fichero (en el modo deseado: de entrada o salida por ejemplo)

mediante el uso de la función open aplicada a ese flujo de entrada, o sea en la forma

descriptor.open(“nombre.extensión”,int modo);

donde la variable modo indica el modo de apertura del fichero y los modos de apertura,

posiblemente combinados mediante el símbolo ‘|’ como veremos a continuación, pueden ser:

ios:: in // Modo entradaios:: out // Modo salidaios:: app // Modo añadir, o sea, posicionar el cursor del fichero (ver abajo)

// al final del fichero antes de cada escrituraios:: binary // El archivo se abre en modo binarioios:: nocreate // Genera un error si el fichero no existeios:: noreplace // Genera un error si el fichero existe ya

Un archivo abierto con ofstream puede serlo en dos modos:

Page 5: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 5

1. Modo salida, usando ios::out. En este caso todos los datos en el fichero se

descartan (o sea, se borra el contenido del fichero si existe previamente).

Naturalmente si el fichero no existe lo crea. En ambos casos el cursor del fichero (o

sea, la cabeza de lectura/escritura del mismo) se posiciona al principio del fichero.

Este es el modo por defecto de apertura de un fichero de salida.

2. Modo añadir, usando ios::app. En este caso los datos adicionales se añaden a

partir del final del fichero (o sea, el cursor se sitúa al final del fichero y es ahí a

partir de donde se empieza a realizar la escritura).

También se pueden combinar las dos formas anteriormente vistas de forma que el descriptor

de fichero es declarado a la vez que el fichero es abierto. Para ello empleamos las siguientes

declaraciones (según el fichero se abra para entrada o para salida):

ifstream descriptor(“nombre.extensión”,int modo); // para entrada

ofstream descriptor(“nombre.extensión”,int modo); // para salida

donde modo es como se ha mostrado anteriormente.

Ejemplo 1:

Las dos líneas siguientes abren el fichero “mio.txt” como fichero de entrada (para lectura) ylo asocian al descriptor in.

ifstream in; // descriptor del fichero a abrir in.open(“mio.txt”); // Apertura del fichero;

Esas dos líneas equivalen a la siguiente:

ifstream in (“mio.txt”); // Apertura del fichero;

Ejemplo 2:

Para abrir el fichero “salida.dat” en modo salida (si el fichero no existe lo crea, y si existeborra su contenido) asociándolo al descriptor out podemos usar la siguiente sentencia;

ofstream out("salida.dat");

o la siguiente

ofstream out("salida.dat", ios::out);

o también

Page 6: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 6

ofstream out; out.open("salida.dat");

9.2.1.2.- Ficheros de entrada/ salida.

Un fichero puede ser también abierto para entrada-salida. En este caso emplearemos una

declaración fstream combinada con la operación de apertura correspondiente, o sea:

fstream descriptor;

descriptor.open(“nombrefichero.ext”, ios::in | ios::out)

O alternativamente podemos combinar la declaración y la apertura en una sola sentencia. Por

ejemplo:

fstream descriptor(“nombre.extensión”,ios::in | ios:: out); // para entrada-salida

La declaración fstream puede ser usada arbitrariamente tanto sobre archivos de escritura

como sobre archivos de lectura, aunque recomendamos que para archivos de solo-lectura se

use ifstream y para aquellos de solo-escritura se use ofstream.

Comentario:

Cuando un fichero de E/S se declara con fstream, abrirlo con open debemos especificar el

modo de apertura que queremos (entrada, salida o bien entrada.salida).

Ejemplo 3:

// Abrimos el fichero “F1.dat” como fichero de entrada fstream inout; inout.open("F1.dat", ios::in);

// Intentamos abrir el fichero “F1.dat” pero tenemos un error puesto que no// hemos especificado el modo de apertura fstream inout; inout.open("F1.dat");

// Abrimos el fichero “F2.txt” como fichero de salida en modo AÑADIR. fstream inout; inout.open("F2.txt", ios::app);

Page 7: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 7

// Abrimos el fichero “binario.dat” para entrada-salida binaria y le asociamos el// descriptor ejemplo. fstream ejemplo ("binario.dat", ios::in | ios:: out | ios::binary);

9.2.2.- Cierre de ficheros.

Un fichero anteriormente abierto y con un descriptor asociado a él debe ser cerrado con

el fin de liberar los recursos asociado a él de la siguiente forma:

descriptor.close()

9.2.3.- Detección de fin de fichero y otras funciones.

Además de las funciones open y close, existen otras funciones disponibles en la

biblioteca fstream, que pueden ser aplicadas directamente al descriptor de un fichero en la

forma

Descriptor.función();

Donde función es una de las siguientes:

• La función eof() que devuelve "true" si se ha alcanzado el final del fichero y falso

en cualquier otro caso.

LECTURA ADELANTADA: Para que la función eof() devuelva un valor de

verdad (actualizado) es necesario, en muchos casos, realizar una operación de

lectura previa. Esto permite que se actualice el valor a devolver por la función eof

comprobándose si tras realizar dicha operación se ha llegado al final del fichero.

A este proceso lo llamaremos lectura adelantada (ver el ejemplo 6).

• La funciones fail() o bad() que devuelven "true" si existe un error en una

operación de flujo asociada al fichero identificado por la variable descriptor

(bien sea en una apertura, un cierre, una escritura o una lectura entre otras

posibles operaciones) y "false" en caso contrario. La diferencia entre estas dos

Page 8: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 8

funciones es muy sutil. En realidad un flujo puede estar en un estado de fallo (i.e.,

fail) pero no en un estado erróneo (i.e. bad) en el caso de que el flujo no se

considere corrupto ya que no se han perdido los caracteres que provienen del

flujo (en un estado erróneo, esto puede no ocurrir). Recomendamos un vistazo a

algún manual de C++ para aclarar las diferencias tan sutiles.

• La función good() que devuelve "true" si no existe un error en una operación de

flujo y "false" en caso contrario.

9.2.4.- Comprobación de apertura correcta

Antes de empezar a leer o escribir en un fichero es siempre conveniente verificar que la

operación de apertura se realizó con éxito. La comprobación del "buen estado" de un

determinado flujo asociado a un fichero se puede realizar preguntando directamente sobre el

descriptor de fichero asociado (al igual que se hace con los flujos estándares cin y cout) en la

forma siguiente:

if (descriptor)

{

//Buen estado. Continuamos

// operando sobre el fichero

.....

}

if (! descriptor)

{

// Mal estado. Escribimos un mensaje

// a la salida estándar

cout << “Error en la apertura “

.....

}

o mejor aun, utilizando las funciones good y bad (o fail) vistas anteriormente, o sea:

if (descriptor.good())

{

//Buen estado. Continuamos

// operando sobre el fichero

.....

}

if (descriptor.bad()) // o descriptor.fail()

{ // Mal estado. Escribimos un mensaje

// a la salida estándar

cout << “Error en la apertura “

}

Ejemplo 4:

ifstream in("F1.dat");if (!in) // O bien, if (in.bad() ){ cout << endl <<"Incapaz de crear o abrir el fichero " << endl;

Page 9: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 9

// salida estándar cout << " para entrada " << endl;}else{ …. // Se opera sobre el fichero}

9.3.- LECTURA-ESCRITURA EN FICHEROS (DE TEXTO).

9.3.1.- Avance del cursor

Tanto para los ficheros de texto como para los ficheros binarios existe lo que se

denomina el cursor o apuntador del fichero que es un índice que direcciona una posición

relativa del fichero. El cursor marca en todo momento la posición actual del fichero a partir

de la cual va a tener lugar la siguiente operación de entrada-salida a ejecutar sobre el mismo

(bien sea una operación de lectura o una de escritura). Cada vez que se realiza una operación

de entrada o salida, el cursor del fichero avanza automáticamente el número de bytes leídos o

escritos.

9.3.2.- Ficheros de texto

La lectura y la escritura en un archivo de texto se puede realizar directamente con los

operadores << y >> al igual que se realiza sobre los flujos estándares cin y cout.

Ejemplo 5:

El siguiente programa escribe tres líneas en un fichero llamado “EJEMPLO5.TXT” que secrea en el programa (si ya existe borramos su contenido). Cada línea consta de un entero, unreal y una cadena de caracteres. Los datos en cada línea están separados por espacios enblanco.

#include <fstream.h> // Biblioteca para el manejo de ficheros#include <iostream.h> // Biblioteca para la entrada-salida estándar

int main(){ ofstream fichout("EJEMPLO5.TXT",ios::out); if (!fichout) { cout << endl << "Incapaz de crear este o abrir el fichero " << endl; } else { fichout << 1 << " " << 5.0 << " APROBADO" << endl; // Escritura en el fichero fichout << 2 << " " << 1.1 << " SUSPENSO" << endl; fichout << 3 << " " << 8.0 << " NOTABLE " << endl; fichout.close(); }} // Fin del main

Page 10: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 10

Comentario:

El operador >> omite los espacios en blanco al igual que ocurría en la entrada estándar.

Ejemplo 6:

El siguiente programa lee el fichero de texto “EJEMPLO5.TXT”, creado en el ejemploanterior, y visualiza su contenido en el monitor.

#include <fstream.h> // Libreria para el manejo de ficheros#include <iostream.h>typedef char TCadena[30];

int main(){ int i; float r; TCadena cad;

ifstream fichin("EJEMPLO5.TXT"); // declaracion y apertura del fichero if (!fichin) { cout << endl << "Incapaz de crear o abrir el fichero "; } else { fichin >> i; // Observe la lectura adelantada!!! while (!fichin.eof()) { cout << i << " "; // Lectura de valores en el fichero fichin >> r; cout << r << " "; // Lectura de valores en el fichero fichin >> cad; cout << cad << endl; // Lectura de valores en el fichero fichin >> i; } fichin.close(); } // Fin del else} // Fin del main

Comentarios:

(1) Observe en el ejemplo anterior que ha sido necesario realizar una lectura adelantada

previamente al chequeo del fin de fichero. Si no se realiza de esta forma podriamos tener

problemas.

(2) Observe también que no es necesario realizar una lectura para “saltarse” los espacios en

blanco que fueron introducidos en el fichero “EJEMPLO5.TXT” en el ejemplo 5. Esto es

debido a que, como ya se comentó anteriormente, el operador >> omite los espacios en

blanco

(3) No olvide cerrar los ficheros!!

9.4.- FICHEROS BINARIOS.

Hasta ahora hemos trabajado con ficheros de texto, éstos pueden ser vistos como una serie de

caracteres que pertenecen a un determinado código de entrada/salida, en nuestro caso al

Page 11: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 11

código ASCII. También hemos visto cómo se abre un fichero binario. En esta sección vamos

a ver cómo podemos leer y escribir datos en binario.

9.4.1.- Utilidad de los ficheros binarios.

En esta sección explicamos la diferencia de los ficheros binarios con los de texto y lo

hacemos a partir de un ejemplo sencillo..

Por ejemplo, si creamos un fichero con

ofstream fich("F1.dat",ios::out);

if (!fich)

{

cout << endl << "Incapaz de crear este o abrir el fichero ";

cout << " para salida " << endl;

}

else

{

fich << "HOLA ADIOS"; // Escribe la cadena en el fichero

// "F1.dat"

fich.close();

}

“F1.dat” será un fichero que contiene 10 bytes, donde cada uno será el código ASCII

correspondiente a cada uno de los caracteres que hemos escrito en el fichero (un carácter

ocupa un byte). Esta característica hace que un fichero de texto pueda ser usado por otros

programas que supongan que el contenido del fichero es un conjunto de caracteres del código

ASCII.

Sabemos que los datos se almacenan en memoria según las características del tipo con

que han sido declarados. Así, para:

char C;unsigned N;

La variable C usa un byte en memoria principal, y en el se almacena el código ASCII de

un carácter, mientras que la variable N, al ser de tipo entero sin signo, usa dos bytes, y en

ellos se almacena el código binario correspondiente a un número entero sin signo. Esta

característica sugiere que para almacenar en el fichero de texto el número contenido en la

Page 12: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 12

variable N no basta con grabar directamente los dos bytes en el fichero (Ejemplo, si N

contiene el número 13864, para almacenarlo en el fichero se requieren 5 caracteres- o sea, 5

bytes-, mientras que en memoria la variable N ocupa cuatro bytes). Así pues, una instrucción

fich << N;

ocasionará dos pasos:

1) Convertir N a su configuración en caracteres ASCII.

2) Escribir en el fichero los caracteres ASCII obtenidos.

En el caso del tipo CHAR, no es necesario efectuar la conversión pues la variable en

memoria principal se corresponde con (es decir, contendrá como valor) el código ASCII

correspondiente al carácter almacenado en la misma.

Hay situaciones en las que en lugar de convertir los valores a escribir en el fichero en una

serie de caracteres, puede resultar útil efectuar la grabación en el fichero directamente a partir

contenido de la memoria. A los ficheros creados de esta forma se les llama ficheros binarios.

Ventajas del uso de ficheros binarios::

• El proceso de lectura ó escritura es más rápido, pues nos ahorramos el tiemponecesario para la conversión.

• Normalmente un fichero binario ocupa menos memoria que su correspondientefichero de texto (Ejemplo, escribir 32534 en formato de texto ocuparía cincobytes, mientras que hacerlo en formato binario ocuparía dos bytes).

Inconvenientes del uso de ficheros binarios:

• No podrán ser usados por programas de carácter general. Por ejemplo, si intento

usar la orden TYPE (de MS-DOS) o la orden cat (de UNIX) con un fichero

binario, la información que obtengo por pantalla no es legible.

De todo lo dicho anteriormente se desprende que es conveniente usar ficheros de texto

cuando pueda interesarnos tratar la información contenida en éstos con programas de carácter

general (Editor, TYPE, PRINT, etc.), y en otro caso usar ficheros binarios.

Tratamiento de ficheros binarios.

Puesto que no hay que distinguir entre diferentes formatos para diferentes tipos de datos,

sólo se necesitan instrucciones que lean y escriban un cierto número de bytes por lo que en

realidad sólo necesitamos un tipo de instrucción para lectura y un tipo de instrucción para

Page 13: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 13

escritura. Aún así veremos dos formas diferentes de leer y escribir desde y en ficheros

binarios.

9.4.2.- Lectura/Escritura byte a byte.

9.4.2.1.- Lectura.

Para leer un carácter (es decir un byte) desde un fichero usamos la función get aplicada sobre

el descriptor del mismo de la siguiente manera

descriptor.get(ch);

Esta instrucción extrae un único carácter del flujo de entrada, incluyendo el espacio en blanco

y lo almacena en la variable ch (que debe ser declarada de tipo carácter). Esta función avanza

además un byte la posición del cursor del archivo identificado por la variable descriptor.

Ejemplo 7:

El siguiente programa lee (byte a byte) un fichero binario que contiene caracteres y visualizasu contenido en el monitor.

#include <fstream.h> // Librería para el manejo de ficheros#include <iostream.h>

int main(){ char c; ifstream fichin("F1.dat",ios::in | ios::binary); if (!fichin) { cout << endl <<"Incapaz de Abrir el fichero "; } else { fichin.get(c); // LECTURA ADELANTADA!! while (fichin) // Tambien vale 'while (!fichin.eof())' { cout << c; fichin.get(c); }; fichin.close(); } // Fin del else

} // Fin del main

9.4.2.2.- Escritura.

Para escribir o mandar un carácter (es decir un byte) a un archivo de de salida usamos la

función put aplicada al descriptor de fichero del archivo de la siguiente manera

Page 14: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 14

descriptor.put(ch);

Esta instrucción coloca el carácter contenido en ch en el archivo de salida identificado por la

variable descriptor (o sea, en su caso en el fichero asociado a dicho descriptor) y lo hace a

partir de la posición actual del cursor. A continuación avanza el cursor del fichero al

siguiente byte.

Ejemplo 8:

El siguiente programa escribe un texto (byte a byte) en el fichero “Ejemplo8.dat”.

#include <fstream.h>#include <iostream.h>

int main(){

char cad[17]="TEXTO A ESCRIBIR"; ofstream fichout("Ejemplo8.dat", ios::out | ios::binary);

if (!fichout) { cout << endl << "Incapaz de Crear o Abrir el fichero "; } else { for (int i=0;i<=16;i++) { fichout.put(cad[i]); // Escritura en el fichero } fichout.close(); } // Fin del else} // Fin del main

9.4.3.- Lectura/Escritura por bloque de bytes.

Otra manera alternativa de leer y escribir en ficheros binarios consiste en utilizar las

funciones read() y write().

9.4.3.1.- Lectura.

La función read tiene varios formatos aunque a continuación explicamos el formato

básico

De forma intuitiva, una llamada de la forma

Page 15: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 15

descriptor.read(&c, num);

donde c es una variable de un tipo arbitrario (por ejemplo de un tipo TElemento), pasada por

referencia, y num es un entero o una variable de tipo entero, ejecuta una lectura (a partir de la

posición actual del cursor del fichero asociado a la variable descriptor) de num bytes del

fichero y los coloca en la variable c. En definitiva la llamada lee num bytes del fichero y los

coloca en la variable c. Esta función puede extraer caracteres hasta alcanzar el fin del fichero.

9.4.3.2.- Escritura.

Asumiendo que c es una variable de un tipo arbitrario (por ejemplo de un tipo TElemento)

pasada por referencia, que num es un entero o una variable conteniendo un entero que

descriptor esta asociado a un fichero, una llamada de la forma

Descriptor.write(&c, num);

Escribe, a partir de la posición del cursor, el contenido de c en el fichero asociado a

descriptor. En realidad sólo escribe num bytes que corresponden a los num bytes siguientes

que se encuentran en memoria a partir de la dirección en la que se encuentra almacenado el

contenido de c.

Esta instrucción escribe a partir de la posición indicada por el puntero de lectura/escritura

del fichero asociado a descriptor los num bytes contenidos en el parámetro c.

Se puede observar que en el segundo parámetro hay que indicar el número de bytes que

ocupa el valor que se desea escribir. Para conocer éste dato de una forma fácil, podremos usar

la función sizeof.

Ejemplo 9:

El siguiente programa declara el fichero “F.dat” para entrada-salida, graba en dicho fichero el

valor 1234.86 en binario y después los veinte primeros enteros. Posteriormente, lee el fichero

visualizando su información en la salida estándar (el monitor).

#include <fstream.h>#include <iostream.h>#include <stdlib.h>

int main(){ float R=1234.86;

Page 16: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 16

int i,N;

fstream fichbin("F.dat",ios::binary | ios::out); // Apertura como salida fichbin.write(&R,sizeof(float)); for (i=1;i<=20;i++) { fichbin.write(&i,sizeof(int)); }

fichbin.close();

fichbin.open("F.dat",ios::binary | ios::in); // Apertura como entrada fichbin.read(&R,sizeof(float)); cout <<endl << "R= " << R << endl; for (i=1;i<=20;i++) { fichbin.read(&N,sizeof(int)); cout <<endl << i << "= " << N << endl; } system("PAUSE"); return 0;} // Fin del main

Observa que la apertura y el cierre de ficheros binarios se puede efectuar con las mismas

operaciones estudiadas para ficheros de texto.

9.5. Acceso aleatorio a ficheros.

Sabemos que las operaciones de lectura y escritura siempre se realizan a partir de la

posición indicada por el puntero de lectura/escritura del fichero. Para poder acceder

correctamente al fichero, debemos saber como cambia dicho puntero de lectura/escritura.

Hasta ahora hemos visto, que tenía un comportamiento secuencial, es decir, siempre se

incrementa en el número de caracteres leídos ó escritos con cada instrucción, no obstante,

puede haber situaciones en las que éste comportamiento no se adapte a nuestras necesidades.

Supongamos que deseamos acceder a información situada cerca del final del fichero.

Según lo visto hasta ahora, debemos leer previamente toda la información que le precede con

objeto de ir avanzando el puntero de lectura/escritura progresivamente. Puesto que

conocemos la situación de la información que queremos leer ó escribir, parece más

conveniente poder situar el puntero de lectura/escritura directamente en dicha posición. Para

gestionar éste tipo de comportamiento disponemos de una serie de funciones.

Ya hemos visto que el acceso a un fichero se describe en términos de lo que

denominamos el cursor o apuntador del fichero que es un índice que apunta a una posición

relativa del fichero de manera que en todo momento la posición actual del cursor marca el

lugar a partir del cual tendrá lugar la operación (de lectura o escritura) sobre el fichero.

Además, cada vez que se realiza una operación de entrada o salida, el cursor del fichero

avanza automáticamente dependiendo del número de bytes leídos o escritos. Básicamente

existen dos instrucciones de acceso aleatorio para cada tipo de acceso a un fichero. Las

Page 17: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 17

vemos a continuación.

9.5.1.- Lectura.

Las funciones de acceso aleatorio para lectura son seekg y tellg.

• seekg(pos) mueve la posición del cursor del fichero a la posición relativa del fichero

indicada por pos, donde pos es un entero (o variable conteniendo un entero).

• tellg() devuelve la posición relativa actual del cursor asociado al fichero de entrada y

devuelve un –1 en caso de existir algún error.

9.5.2.- Escritura.

Las funciones de acceso aleatorio equivalentes para escritura on seekp y tellp.

• seekp(pos) mueve la posición del cursor del fichero a la posición absoluta del fichero

indicada por pos.

• tellp() devuelve la posición absoluta actual del cursor asociado al fichero de salida y

devuelve un –1 en caso de existir algún error.

Ejemplo 10:

El siguiente programa almacena en un fichero los 10 primeros enteros, luego muestra por

pantalla el quinto entero (o sea el 5), posteriormente lo reemplaza por el valor 100, y al final

visualiza en el monitor el contenido del fichero.

#include <fstream.h>#include <iostream.h>#include <stdlib.h>

int main(){ int i,N;

fstream fichbin("ejemplo11.dat",ios::binary | ios::in | ios::out); for (i=1;i<=10;i++) { fichbin.write(&i,sizeof(int)); } // Almacena los 10 primeros enteros

fichbin.seekp(4*sizeof(int)); // se posiciona al principio del quinto entero

fichbin.read(&N,sizeof(int)); // Lee dicho entero cout <<endl << "Quinto= " << N << endl; // visualiza el valor 5

fichbin.seekp(4*sizeof(int)); // se posiciona de nuevo al principio del quinto entero // pues el cursor había avanzado. i=100; fichbin.write(&i,sizeof(int)); // Modifica el valor 5 por el valor 100;

Page 18: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 18

fichbin.seekp(0*sizeof(int)); // se posiciona de nuevo al principio del fichero for (i=1;i<=10;i++) { fichbin.read(&N,sizeof(int)); cout <<endl << i << "= " << N << endl; // Se visualiza el contenido en pantalla } fichbin.close(); system("PAUSE"); return 0;} // Fin del main

9.6.- Ficheros pasados como argumentos.

En esta sección simplemente comentamos que cuando el descriptor de un fichero es

pasado como argumento, la mayoría de las veces debe hacerlo como parámetro por referencia

puesto que cualquier operación de lectura/escritura (y la mayoría de las de acceso directo)

modifican la posición del cursor asociado a ese fichero.

Ejemplo 11:

El siguiente programa simplemente añade la cadena "HOLA" al fichero "poesia.txt". Observe

que el argumento formal del procedimiento escribir_hola esta declarado por referencia.

#include <fstream.h>

void escribir_hola(fstream &canal_salida){ canal_salida << endl << "HOLA" << endl;}

int main(){ fstream out("poesia.txt",ios::app); escribir_hola(out); out.close();} // Fin del main

9.7.- Ejemplos de utilización de ficheros binarios y de texto.

En esta sección mostramos dos ejemplos de cómo podemos leer/escribir datos desde/a

ficheros binarios o de texto.

Comentario:

Observe que en la apertura de ficheros binarios ES NECESARIO, en algunos compiladores

como es el caso del DEV-C++) especificar el modo de apertura (in, out, app, ...,etc) a pesar

de que este sea el modo por defecto. Si no se especifica entonces podemos tener problemas

en la lectura de los datos.

Page 19: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 19

Ejemplo 12:

El siguiente programa lee, desde teclado una información relativa a una serie de empleadosde una empresa. Los datos a leer son el nombre, un código formado por cuatro enteros - ojo,no por cuatro dígitos- y una cantidad correspondiente al sueldo mensual. Entonces, toda lainformación recogida es grabada en dos ficheros (uno de texto y otro binario) para serposteriormente leída desde los mismos y visualizada en pantalla.

#include <iostream.h>#include <stdlib.h>#include <fstream.h>

//CONSTANTESconst int MAXCAD = 40;const int MAXVECT = 4;const char ENTER = '\n';

//TIPOStypedef int TVector[MAXVECT];typedef char TCadena[MAXCAD+1];struct TPersona{ TCadena nombre; TVector codigo; // exactamente cuatro cifras float sueldo;};void VisualizaPersonasFichero(TCadena nombreFichero,int numempleados);void EscribePersonaFichero(TCadena nombreFichero, TPersona p);void VisualizaPersonasFicheroBin(TCadena nombreFichero,int numempleados);void EscribePersonaFicheroBin(TCadena nombreFichero, TPersona p);void pintaVector(TVector v);void pintaPersona(TPersona p);void leePersona(TPersona &p);

int main(){ TPersona per; TVector cod; int i,N;

cout << "introduzca el número de personas a incluir "; cin >> N; for (i=1; i <= N ; i ++) { leePersona(per); // Escribo la información en texto y en binario EscribePersonaFichero("empleados.txt",per); EscribePersonaFicheroBin("empleados.dat",per); }

cout << "Mostramos la información almacenada" << endl; cout << "Visualizo desde el fichero de texto: " << endl; VisualizaPersonasFichero("empleados.txt",N); cout << endl;

cout << "Visualizo desde el fichero binario: " << endl; VisualizaPersonasFicheroBin("empleados.dat",N); cout << endl;

system("PAUSE"); return 0;} // Fin del main

Page 20: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 20

// Este proc. visualiza la información almacenada en el fichero de textovoid VisualizaPersonasFichero(TCadena nombreFichero, int numempleados){ ifstream in; int i,j; TPersona p;

in.open(nombreFichero); if (in.bad()) // Comprobamos el estado de la apertura { cout << "Error al abrir el fichero: " << nombreFichero << endl; } else { for (i=1;i<=numempleados; i++) { in.getline(p.nombre,MAXCAD+1,ENTER); //USO DE getline en el fichero for (j=0;j<MAXVECT;j++) { in >> p.codigo[j]; }

in >> p.sueldo; pintaPersona(p); in.ignore(); // Uso de ignore() sobre el descriptor } in.close(); }} // Fin VisualizaPersonasFichero

// El siguiente proc. Escribe la informacion de una persona en un ficherode textovoid EscribePersonaFichero(Tcadena nombreFichero, TPersona p){ ofstream out; int i;

out.open(nombreFichero,ios::app); if (out.bad()) { cout << "Error al abrir el fichero: " << nombreFichero << endl; } else { out << p.nombre << endl;

for(i=0;i<MAXVECT;++i) { out << p.codigo[i] << ' '; // Ojo, incluimos un espacio en medio!! }

out << p.sueldo << endl; out.close(); }} // Fin EscribePersonaFichero

// Este proc. visualiza la información almacenada en el fichero binariovoid VisualizaPersonasFicheroBin(TCadena nombreFichero,int numempleados){ ifstream in; int i; Tpersona p;

in.open(nombreFichero, ios::in | ios::binary); // Ojo, hay que poner 'in' forzosamente

if (in.bad()) {

Page 21: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 21

cout << "Error al abrir el fichero: " << nombreFichero << endl; } else { for (i=1;i<=numempleados;i++) { in.read(&p,sizeof(TPersona)); // Se lee toda la información !! pintaPersona(p); } in.close(); }} // Fin VisualizaPersonasFicheros

// El siguiente proc. Escribe la informacion de una persona en un ficherobinariovoid EscribePersonaFicheroBin(TCadena nombreFichero, TPersona p){ ofstream out;

out.open(nombreFichero, ios::binary | ios::app); // Ojo, especificamos el modo AÑADIR

if (out.bad()) { cout << "Error al abrir el fichero: " << nombreFichero << endl; } else { out.write(&p,sizeof(TPersona)); out.close(); }} // Fin EscribePersonaFicheroBin

// El siguiente proc. visualiza un código de cuatro enteros (o sea, unvector) a la pantallavoid pintaVector(TVector v){ int i; cout << "CODIGO : { "; for(i=0;i<MAXVECT-1;++i) { cout << v[i] << ", " ; } cout << v[MAXVECT-1] << " } " << endl;}

// El siguiente proc. Visualiza la información de una persona en lapantallavoid pintaPersona(Tpersona p){ cout << "NOMBRE : " << p.nombre << " "; pintaVector(p.codigo); cout << "SUELDO : " << p.sueldo << endl;}

// El siguiente proc, lee información de una persona desde el tecladovoid leePersona(TPersona &p){ int i; cin.ignore(); // Para limpiar el buffer de teclado cout << "Nombre ? "; cin.getline(p.nombre,MAXCAD+1,ENTER);

cout << endl << "Codigo (4 enteros-NO cifras solo) ?"; for(i=0;i<MAXVECT;++i) { cin >> p.codigo[i]; }

Page 22: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 22

cout << endl << "Sueldo (euros) ?"; cin >> p.sueldo;}

Comentarios:

1. El primer argumento de read y write no necesita el símbolo & cuando es de tipo

array como se ve en el siguiente ejemplo.

2. Observe que es posible usar ignore() y getline() sobre el descriptor de un

fichero y que funcionan de manera análoga a su aplicación sobre cin.

Ejemplo 13:El siguiente programa escribe el vector que contiene la información {0,1,2,3,....,8,9} tanto enun fichero binario como en uno de texto.

#include <iostream.h>#include <stdlib.h>#include <fstream.h>

//CONSTANTESconst int MAXCAD = 20;const int MAXVECT = 10;

//TIPOStypedef int TVector[MAXVECT];typedef char TCadena[MAXCAD+1];

void LeeVectorFichero(TCadena nombreFichero, TVector &v);void EscribeVectorFichero(TCadena nombreFichero, TVector v);void LeeVectorFicheroBin(TCadena nombreFichero, TVector &v);void EscribeVectorFicheroBin(TCadena nombreFichero, TVector v);void pintaVector(TVector v);

int main(){ TVector v; int i;

for(i=0;i<MAXVECT;++i) // Inicializo el vector { v[i]=i; } cout << "Inicializo el vector y lo escribo " << "en ficheros de texto y binario: " << endl; pintaVector(v); EscribeVectorFichero("vector.txt",v); EscribeVectorFicheroBin("vector.dat",v);

for(i=0;i<MAXVECT;++i) // Borro el vector { v[i]=0; } cout << "Reseteo el contenido del vector:" << endl; pintaVector(v);

cout << "Leo v desde el fichero de texto: " << endl;

Page 23: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 23

LeeVectorFichero("vector.txt",v); pintaVector(v);

cout << "Leo v desde el fichero binario: " << endl; LeeVectorFicheroBin("vector.dat",v); pintaVector(v);

system("PAUSE"); return 0;}

// Lee un vector desde un fichero de textovoid LeeVectorFichero(TCadena nombreFichero, TVector &v){ ifstream in; int i;

in.open(nombreFichero); if (in.bad()) // Comprobamos el estado de la apertura { cout << "Error al abrir el fichero: " << nombreFichero << endl; } else { for(i=0;i<MAXVECT;++i) { in >> v[i]; }

in.close(); }}

// Escribe un vector en un fichero de textovoid EscribeVectorFichero(TCadena nombreFichero, TVector v){ ofstream out; int i;

out.open(nombreFichero);

if (out.bad()) { cout << "Error al abrir el fichero: " << nombreFichero << endl; } else { for(i=0;i<MAXVECT;++i) { out << v[i] << " "; }

out.close(); }}

// Lee un vector desde un fichero binariovoid LeeVectorFicheroBin(TCadena nombreFichero, TVector &v){ ifstream in;

in.open(nombreFichero);

if (in.bad()) { cout << "Error al abrir el fichero: " << nombreFichero << endl; } else { in.read(v,sizeof(TVector)); in.close(); }}

Page 24: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 24

// Escribe un vector a un fichero binariovoid EscribeVectorFicheroBin(TCadena nombreFichero, TVector v){ ofstream out;

out.open(nombreFichero);

if (out.bad()) { cout << "Error al abrir el fichero: " << nombreFichero << endl; } else { out.write(v,sizeof(TVector)); out.close(); }}

// Visualiza un vector a la pantallavoid pintaVector(TVector v){ int i;

cout << " v = { "; for(i=0;i<MAXVECT-1;++i) { cout << v[i] << ", " ; } cout << v[MAXVECT-1] << " } " << endl;

}

Una vez ejecutado el programa la salida es la siguiente:

Inicializo el vector y lo escribo en ficheros de texto y binario: v ={0,1,2,3,4,5,6,7,8,9} Reseteo el contenido del vector v={0,0,0,0,0,0,0,0,0,0} Leo v desde el fichero de texto v ={0,1,2,3,4,5,6,7,8,9} Leo v desde el fichero binario v ={0,1,2,3,4,5,6,7,8,9} Presione cualquier tecla para continuar...........

Page 25: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 25

EJERCICIOS

1.- Escribir un programa con la opción de encriptar y de desencriptar un fichero de texto.La encriptación (codificación) consiste en que dado un fichero de texto de entrada genere otrofichero de salida de extensión <nombre>.COD donde el texto estará codificado (encriptado).Esta codificación consiste en reemplazar cada carácter por el tercero siguiente (ej. a -> d). Siel carácter resultante es no imprimible éste no se cambia. La opción de desencriptado consisteen leer un fichero <nombre>.COD y recuperar la información original.

2.- Escriba un programa que cree un fichero de texto llamado "PERSONAS.DAT" en elque se guarde la información de un número indeterminado de personas. La información quese guardará por cada persona será:

Nombre: De 1 a 30 caracteres.Edad CARDINAL.Sexo CHAR (M/F).Arte CHAR (S/N).Deporte CHAR (S/N).Libros CHAR (S/N).MúsicaCHAR (S/N).

La información correspondiente a cada persona se leerá del teclado. El procesofinalizará cuando se teclee un campo "Nombre" que esté vacío.

3.- Amplíe el programa que procesa clientes de una agencia matrimonial para que tomelos datos de todos los candidatos a estudiar del fichero PERSONAS.DAT del ejercicioanterior, lea el cliente del teclado y finalmente genere como resultado un listado por pantallacon los nombres de los candidatos aceptados y un fichero llamado ACEPTADOS.DAT contoda la información correspondiente a los candidatos aceptados. Una persona del ficheroPERSONAS.DAT se considerará aceptable como candidato si tiene diferente sexo y por lomenos tres aficiones comunes con respecto al aspirante introducido por pantalla. (Elprograma debe ser capaz de trabajar con cualquier número de personas en el ficheroPERSONAS.DAT).

4.- Codifique un programa que cree un fichero para contener los datos relativos a losartículos de un almacén. Para cada artículo habrá de guardar la siguiente información:

Código del artículo (Numérico)Nombre del artículo (Cadena de caracteres)Existencias actuales (Numérico)Precio (Numérico).

Se deberán pedir datos de cada artículo por teclado hasta que como código se teclee 0.El fichero se habrá de crear ordenado por el código del articulo.

5.- Escriba un programa que dados dos ficheros generados por el programa anterior yordenados genere un tercer fichero que sea el resultado de mezclar de formar ordenada losdos primeros.

6.- Escriba un programa que tome como entrada el fichero del ejercicio 4 y una condiciónsobre los campos Existencias actuales o Precio. La condición podrá ser:

Page 26: ficheros en c++

___________________________________________________________________________Laboratorio de Programación GESTION BASICA DE FICHEROS Pág 26

<campo> [<,<=,>,>=, != ] <número>

Este programa debe generar como salida un fichero llamado "salida.dat" que contenga todosaquellos artículos para los que se cumple la condición de entrada.

7.- Escriba un programa que tenga como entrada un fichero que contenga un texto y décomo resultado una estadística de las palabras que hay de cada longitud, así como de loscaracteres especiales (",", ".", ":", ";").

8.- Escribir un programa que genere a partir de un fichero de entrada que contiene unatabla de números reales otro fichero de salida <nombre>.BIN grabado en formato binario.

Ej: 1.23 3.45 12.54.8 3.9 0.83........................

9.- Dado una tabla grabada en un fichero en formato binario <nombre>.BIN hallar lasuma horizontal y vertical y grabar la tabla y los resultados en otro fichero de texto o binariosegún se introduzca por pantalla. El resultado en texto sería el siguiente:Ej: 1.23 3.45 12.5 17,18

4.8 3.9 0.83 9,536,03 7,35 13,33