Practica N°06
Practica N°06
Practica N°06
OBJETIVO
______________________________________________________________________
❖ La presente práctica de laboratorio tiene como objetivo trabajar con ficheros en C++.
TEMA
______________________________________________________________________
MARCO TEÓRICO
______________________________________________________________________
Los archivos o ficheros son la forma en la que C++ permite el acceso al disco. Todos
los procesos tienen abiertos, por defecto, los archivos 0(entrada), 1(salida) y 2(salida
de errores), de manera que en C++ se corresponden con los objetos cin, cout y cerr.
De estos últimos, el primero pertenece a la clase ifstream, que a su vez desciende de
istream (flujo de entrada). Los dos últimos pertenecen a la clase ofstream, que
desciende de la clase ostream (flujo de salida). Una jerarquía aproximada puede verse
a continuación.
Usar streams facilita mucho el acceso a ficheros en disco, veremos que una vez que
creemos un stream para un fichero, podremos trabajar con él igual que lo hacemos
con cin o cout.
Mediante las clases ofstream, ifstream y fstream tendremos acceso a todas las
funciones de las clases base de las que se derivan estas: ios, istream, ostream,
fstreambase, y como también contienen un objeto filebuf, podremos acceder a las
funciones de filebuf y streambuf.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
char cadena[128];
// Crea un fichero de salida
ofstream fs("nombre.txt");
return 0;
}
Veamos otro ejemplo sencillo, para ilustrar algunas limitaciones del operador >> para
hacer lecturas, cuando no queremos perder caracteres.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
char cadena[128];
ifstream fe("streams.cpp");
while(!fe.eof()) {
fe >> cadena;
cout << cadena << endl;
}
fe.close();
return 0;
}
El resultado quizá no sea el esperado. El motivo es que el operador >> interpreta los
espacios, tabuladores y retornos de línea como separadores, y los elimina de la
cadena de entrada.
❖ FICHEROS BINARIOS:
Los ficheros binarios son más útiles para guardar información cuyos valores no estén
limitados. Por ejemplo, para almacenar imágenes, o bases de datos. Un fichero binario
permite almacenar estructuras completas, en las que se mezclen datos de cadenas con
datos numéricos.
En realidad, no hay nada que nos impida almacenar cualquier valor en un fichero de
texto, el problema surge cuando se almacena el valor que el sistema operativo usa
para marcar el fin de fichero en un archivo de texto. En MS-DOS ese valor es 0x1A.
Si abrimos un fichero en modo de texto que contenga un dato con ese valor, no nos
será posible leer ningún dato a partir de esa posición. Si lo abrimos en modo binario,
ese problema no existirá.
Los ficheros que hemos usado en los ejemplos anteriores son en modo texto, veremos
ahora un ejemplo en modo binario:
#include <iostream>
#include <fstream>
#include <cstring>
struct tipoRegistro {
char nombre[32];
int edad;
float altura;
};
int main() {
tipoRegistro pepe;
tipoRegistro pepe2;
ofstream fsalida("prueba.dat",
ios::out | ios::binary);
fsalida.write(reinterpret_cast<char *>(&pepe),
sizeof(tipoRegistro));
fsalida.close();
ifstream fentrada("prueba.dat",
ios::in | ios::binary);
fentrada.read(reinterpret_cast<char *>(&pepe2),
sizeof(tipoRegistro));
cout << pepe2.nombre << endl;
cout << pepe2.edad << endl;
cout << pepe2.altura << endl;
fentrada.close();
return 0;
}
Otro par de funciones relacionadas con el acceso aleatorio son tellp y tellg, que sirven
para saber en qué posición del fichero nos encontramos.
#include <fstream>
using namespace std;
int main() {
int i;
char mes[][20] = {"Enero", "Febrero", "Marzo",
"Abril", "Mayo", "Junio", "Julio", "Agosto",
"Septiembre", "Octubre", "Noviembre",
"Diciembre"};
char cad[20];
ofstream fsalida("meses.dat",
ios::out | ios::binary);
// Acceso secuencial:
cout << "\nAcceso secuencial:" << endl;
fentrada.read(cad, 20);
do {
cout << cad << endl;
fentrada.read(cad, 20);
} while(!fentrada.eof());
fentrada.clear();
// Acceso aleatorio:
cout << "\nAcceso aleatorio:" << endl;
for(i = 11; i >= 0; i--) {
fentrada.seekg(20*i, ios::beg);
fentrada.read(cad, 20);
cout << cad << endl;
}
return 0;
}
La función seekg nos permite acceder a cualquier punto del fichero, no tiene por qué
ser exactamente al principio de un registro, la resolución de las funciones seek es de
un byte.
Cuando trabajemos con nuestros propios streams para nuestras clases, derivándolas
de ifstream, ofstream o fstream, es posible que nos convenga sobrecargar las
funciones seek y tell para que trabajen a nivel de registro, en lugar de hacerlo a nivel
de byte.
Para eso usaremos la clase fstream, que al ser derivada de ifstream y ofstream, dispone
de todas las funciones necesarias para realizar cualquier operación de entrada o salida.
Hay que tener la precaución de usar la opción ios::trunc de modo que el fichero sea
creado si no existe previamente.
Este programa crea un fichero con una palabra, a continuación, lee todo el fichero y
cambia todos los caracteres 'a' por 'e'. Finalmente muestra el resultado. Básicamente
muestra cómo trabajar con ficheros simultáneamente en entrada y salida.
#include <fstream>
using namespace std;
int main() {
char l;
long i, lon;
fstream fich("prueba.dat", ios::in |
ios::out | ios::trunc | ios::binary);
fich.seekg(0L, ios::end);
lon = fich.tellg();
for(i = 0L; i < lon; i++) {
fich.seekg(i, ios::beg);
fich.get(l);
if(l == 'a') {
fich.seekp(i, ios::beg);
fich << 'e';
}
}
cout << "Salida:" << endl;
fich.seekg(0L, ios::beg);
for(i = 0L; i < lon; i++) {
fich.get(l);
cout << l;
}
cout << endl;
fich.close();
return 0;
}
❖ ACTIVIDADES
______________________________________________________________________
• Consideraciones: Puede utilizar como IDE: Zinjal, Visual Studio 2019, entre
otros. Ello debe ser especificado en su informe.
Nota: Para los siguientes ejercicios, programar de forma ordenada de tal manera que
las clases y/o estructuras queden definidas en archivos cabecera (.h), los métodos u
operaciones en un archivo .cpp y finalmente el programa principal en otro archivo
.cpp. Asimismo, debe utilizar la experiencia de la práctica anterior y debe estar visible
en el código.
• EJERCICIOS PROPUESTOS:
struct Tdato
{
int b;
char s[100];
};
int x, n, a[10]={1,2,3,4,5,6,7,8,9,0};
double f;
char nombre[]="Ejercicios ficheros binarios";
Tdato p;
ifstream f1;
ofstream f2;
f1.open("entrada.dat", ios::binary);
f2.open("salida.dat", ios::binary);
Escribe las instrucciones para realizar cada una de las siguientes operaciones:
a. Escribir el dato entero x en el fichero.
b. Escribir el dato double f en el fichero.
c. Escribir los 5 primeros elementos del array a en el fichero.
d. Escribir los 10 primeros caracteres de la cadena nombre en el fichero.
e. Escribir el dato de tipo Tdato p en el fichero.
f. Leer un dato entero del fichero y almacenarlo en la variable x.
g. Leer un dato double del fichero y almacenarlo en la variable f.
h. Leer 5 enteros y almacenarlos en el array a.
i. Leer 10 caracteres y almacenarlos en la cadena nombre.
j. Leer un dato de tipo Tdato y almacenarlo en la variable p.
3. Crear un programa que pida al usuario que teclee frases, y las almacene en el
fichero “frases.txt”. Acabará cuando el usuario pulse Intro sin teclear nada.
Después deberá mostrar el contenido del fichero.
5. Crear una agenda que maneje los siguientes datos: nombre, dirección, tlf
móvil, email, y día, mes y año de nacimiento (estos tres últimos datos deberán
ser números enteros cortos). Deberá tener capacidad para 100 fichas. Se
deberá poder añadir un dato nuevo, visualizar los nombres de las fichas
existentes, o mostrar todos los datos de una persona (se preguntará al usuario
cual es el nombre de esa persona que quiere visualizar). Al empezar el
programa, leerá los datos de un fichero llamado “agenda.dat” (si existe). Al
terminar, guardará todos los datos en ese fichero.
❖ BIBLIOGRAFIA
______________________________________________________________________
➢ Deitel, Paul J., Deitel, Harvey M., “Cómo Programar en C++”, 6ta Edición, Ed.
Pearson Educación, México 2009.