Archivos, Archivos de texto

Anuncio
ESCUELA DE INGENIERÍA DE SISTEMAS
DEPARTAMENTO DE COMPUTACIÓN
PROGRAMACIÓN 2
PRÁCTICA DE LABORATORIO 12
Archivos, Archivos de texto
Contenido
Introducción .................................................................................................... 1
Archivos y flujos en C++ ................................................................................. 1
Archivos de encabezado................................................................................. 2
Abrir Archivos.................................................................................................. 2
Modos de apertura de archivo ........................................................................ 4
Cerrar archivos ............................................................................................... 5
Lectura y Escritura de un Archivo de texto...................................................... 5
Estado de las operaciones de E/S sobre archivos.......................................... 9
Ejercicio .......................................................................................................... 9
Introducción
Un archivo es un grupo de registros relacionados almacenados en
memoria secundaria. Por ejemplo, un archivo de nómina de una
compañía contiene normalmente un registro por cada empleado. De
esta manera, el archivo de nómina de una compañía pequeña podría
contener sólo 20 registros y, en cambio un archivo de nómina de
una compañía grande podría contener hasta 50.000 registros.
La ventaja es que la información almacenada en un archivo es
persistente en el tiempo, no es susceptible a fallas eléctricas y
puede reproducirse y transportarse a bajo costo.
Archivos y flujos en C++
C++ ve a cada archivo simplemente como una secuencia de bytes que
termina con un marcador especial de fin de archivo. Las
bibliotecas estándar de C++ proporcionan un amplio conjunto de
capacidades de entrada/salida.
En general, la E/S en C++ se da en flujo de bytes. Un flujo es
simplemente una secuencia de bytes. En las operaciones de entrada,
los bytes fluyen desde un dispositivo (por ejemplo: teclado,
unidad de disco, etc.) hacia la memoria principal. En operaciones
de salida los bytes fluyen de la memoria principal hacia un
dispositivo (por ejemplo: pantalla, impresora, unidad de disco,
etc.). Ya se ha visto que existen cuatro objetos que se crean
automáticamente cuando se utiliza la biblioteca iostream: cin,
cout, cerr y clog. Estos objetos asocian canales de comunicación
entre un programa y un archivo o dispositivo particular. Por
ejemplo, el objeto cin (flujo de entrada estándar) permite que un
programa reciba datos desde el teclado; el objeto cout (flujo de
salida estándar) permite que un programa envíe datos a la
pantalla, y los objetos cerr y clog (flujo de error estándar)
permiten que un programa envíe mensajes de error a la pantalla.
Archivos de encabezado
Para realizar el procesamiento de archivos en C++ se deben incluir
los archivos de encabezado <iostream.h> y <fstream.h>. El archivo
<fstream.h> incluye las definiciones para las clases ifstream
(para entrada desde un archivo), ofstream (para salida hacia un
archivo) y fstream (para entrada y salida de un archivo).
ios
istream
ifstream
ostream
iostream
ofstream
fstream
Para crear un flujo para leer/escribir archivos a disco (archivos
físicos), lo primero que debe hacer es definir un objeto (llamado
también archivo lógico) para una de las clases de archivo
convenientemente:
• la clase ifstream, para definir los archivos que se usan
exclusivamente para entrada de datos (leer).
• la clase ofstream, para definir los archivos que se usan
exclusivamente para la salida de datos (escribir).
• la clase fstream, para definir archivos de entrada y/o salida
de datos.
El siguiente programa muestra la forma de crear estos objetos de
la clase fstream:
#include <fstream>
using namespace std;
int main(){
ifstream lectura; // para leer un archivo
ofstream escritura; // para escribir un archivo
fstream lecturaEscritura; // para leer y escribir un archivo
return 0;
}
Abrir Archivos
El método open(), heredado por todas las clases del flujo de
archivos, permite asociar el objeto del flujo de archivos (archivo
lógico) a un archivo físico en disco y luego abrirlo para su
acceso. La forma de llamar a este método es:
<objeto de flujo de archivo>.open (<nombre del archivo físico>,
<modo de apertura de archivo>);
El nombre del objeto es seguido por un punto y después por la
función open() y luego sus argumentos. Los argumentos son: un
nombre del archivo en disco y un designador del modo de apertura
(opcional). El nombre del archivo en disco se debe ajustar a los
requerimientos del sistema operativo. El nombre del archivo en el
disco físico se puede especificar directamente dentro de dobles
comillas (por ejemplo “ejemplo.txt”) o en forma indirecta como una
variable tipo cadena. El argumento indicador del modo de apertura
define que tipo de acceso se realizará dentro del archivo
(detallado en la siguiente sección).
Nota: El (los) modo(s) de lectura/escritura siempre debe(n)
especificarse cuando un objeto de flujo de archivos se define con
la clase fstream, pues por definición, esta clase se usa para
ambos accesos a archivos de entrada y salida (lectura/escritura)
Los modos lectura/escritura no necesitan especificarse cuando se
abren archivos definidos por las clases ifstream u ofstream,
porque los archivos son predeterminados como entrada y salida
respectivamente.
Compile y ejecute el siguiente programa y luego verifique los
archivos creados en el directorio. ¿Por qué solo se crea el
archivo ejemplo2.txt?
#include <fstream>
using namespace std;
int main(){
ifstream lectura; // para leer un archivo físico
ofstream escritura; // para escribir un archivo físico
fstream lecturaEscritura; // para leer y escribir un archivo
// Archivo logico lectura que lea del archivo de disco ejemplo1.txt.
lectura.open("ejemplo1.txt");
// Archivo lógico escritura que escribe al archivo de disco ejemplo2.txt.
escritura.open("ejemplo2.txt");
// Archivo logico lescturaEscritura que lea y escriba el archivo ejemplo3.txt.
lecturaEscritura.open("ejemplo3.txt", ios::in | ios::out);
return 0;
}
Después de crear los objetos y abrirlos, se recomienda verificar
si la operación tuvo éxito a través del operador sobrecargado ! de
ios, retornando true si la operación falla. Algunos posibles
errores son: intentar abrir un archivo que no existe en modo
lectura, abrir un archivo de lectura sin permiso y abrir un
archivo para escritura si no hay espacio en disco.
El siguiente código realiza esta verificación (compile y ejecute):
#include <iostream>
#include <fstream>
using namespace std;
int main(){
ifstream lectura; // para leer un archivo
ofstream escritura; // para escribir un archivo
fstream lecturaEscritura; // para leer y escribir un archivo
lectura.open("ejemplo1.txt");
escritura.open("ejemplo2.txt");
lecturaEscritura.open("ejemplo3.txt", ios::in | ios::out);
if (!lectura) cout << "No se puede abrir el acrhivo lectura -> ejemplo1.txt" << endl;
if (!escritura) cout << "No se puede abrir el acrhivo
escritura --< ejemplo2.txt" << endl;
if (!lecturaEscritura) cout << "No se puede abrir el acrhivo
lecturaEscritura --> ejemplo3.txt" << endl;
return 0;
}
Los constructores paramétricos de las clases fstream, ifstream y
ofstream
además
de
crear
el
objeto
del
archivo
físico
correspondiente, permiten abrir un archivo al igual que el método
open(). El siguiente código lo muestra (compile y ejecute):
#include <iostream>
#include <fstream>
using namespace std;
int main(){
ifstream lectura("ejemplo1.txt");
ofstream escritura("ejemplo2.txt");
fstream lecturaEscritura("ejemplo3.txt", ios::in | ios::out);
if (!lectura) cout << "No se puede abrir el acrhivo lectura -> ejemplo1.txt" << endl;
if (!escritura) cout << "No se puede abrir el acrhivo
escritura --< ejemplo2.txt" << endl;
if (!lecturaEscritura) cout << "No se puede abrir el acrhivo
lecturaEscritura --> ejemplo3.txt" << endl;
return 0;
}
Modos de apertura de archivo
Dependiendo del propósito del programa debe indicar el modo de
acceso, por ejemplo es posible que sea necesario agregar datos a
los ya existentes en el archivo, o quizá que las operaciones del
programa no se lleven a cabo en caso de que el archivo
especificado exista en el disco, etc., Dependiendo del caso
podemos especificar el modo de apertura según la siguiente tabla:
Modo
ios::app
Descripción
Anexa toda la salida al final del archivo
ios::ate
ios::in
ios::out
ios::nocreate
ios::noreplace
ios::trunc
ios::binary
Abre en modo salida y se coloca el apuntador del
archivo al final del mismo. Los datos pueden
escribirse en cualquier parte del archivo
Operaciones de lectura. Esta es la opción por
defecto para objetos de la clase ifstream.
Operaciones de escritura. Esta es la opción por
defecto para objetos de la clase ofstream.
Si el archivo no existe se suspende la operación
Crea un archivo, si existe uno con el mismo
nombre la operación se suspende.
Crea un archivo, si existe uno con el mismo
nombre lo borra.
Operaciones binarias (no es texto)
Cerrar archivos
Después de terminar el procesamiento del archivo, deberá cerrar el
archivo para garantizar su contenido y permitir su posterior uso.
Esto se realiza con la función close() Para cerrar un archivo, lo
que necesita hacer es llamar al método close() con un objeto de
flujo
de
archivo.
Como
resultado,
los
enunciados
lecturaEscritura.close(),
escritura.close()
y
lectura.close()
deberán cerrar los archivos que se abrieron anteriormente.
Lectura y Escritura de un Archivo de texto
Se escribe un archivo en disco usando su objeto de flujo de
archivo de salida y el operador de inserción <<, de la misma forma
como se usó el objeto de flujo de archivo estándar cout y el
operador <<. De esta manera, luego de abrir un objeto de archivo
de salida las siguientes instrucciones permiten escribir:
ofstream salida;
salida.open(“ejemplo.txt”);
salida << “Esto es un ejemplo” << endl;
salida.close();
Verifique en su disco que aparece el archivo “ejemplo.txt” y que
contiene el texto. Se pruebe escribir cualquier tipo de variable
(string, arreglo de caracteres, entero, etc.) que tenga definido
el operador <<. El siguiente programa genera un archivo de texto
donde cada línea contiene un número de línea correspondiente al
valor inicial del número de líneas solicitado al usuario (copie,
compile, ejecute y verifique el archivo creado con su contenido):
#include <iostream>
#include <fstream>
using namespace std;
int main(){
int numl,i;
ofstream salida; // flujo de salida
// Apertura del archivo, crear y abrir un nuevo archivo
salida.open("ejemplo-escribir.txt");
// Verificando la operacion -- si el archivo existe o no
if (!salida)
{
cout<<"No se puede abrir el archivo\n";
exit(1);
}
else
{
cout<<"Suministre el total de lineas del archivo: ";
cin>>numl;
for (i=0;i<numl;i++)
{
//Escritura de un archivo de texto
salida << i+1 <<"\n";
}
}
salida.close(); //Cierre del archivo
cout<<"****** Archivo Procesado ******\n";
return 0;
}
Se lee un archivo en disco con el objeto de flujo de archivo de
entrada y el operador de extracción >>, tal como se usa el objeto
de flujo de archivo estándar cin y el operador >>. De esta manera,
luego de abrir un objeto de archivo de entrada las siguientes
instrucciones permiten leer la primera secuencia de caracteres
delimitada por el carácter espacio en blanco:
string cadena;
ifstream entrada;
entrada.open(“ejemplo.txt”);
entrada >> cadena;
cout << cadena << endl; // muestra lo leído del archivo
entrada.close();
Ejecute el código anterior y verifique lo que muestra por
pantalla, note que no es toda la línea que contiene el archivo
ejemplo.tx. Note que la cadena se definió como un string por tanto
debe incluir la clase string de C++. Si desea obtener toda una
línea del archivo de entrada (independientemente del número de
palabras) debe usarse la función getline, en lugar de “entrada >>
cadena”, de la siguiente forma:
getline(entrada, cadena, ‘\n’);
Copie, compile y ejecute el siguiente código que muestra como leer
el archivo ejemplo-escribir.txt línea a línea y mostrarlo por
pantalla.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(){
string i;
ifstream entrada; // flujo de entrada
// Apertura del archivo: abrir un nuevo existente)
entrada.open("ejemplo-escribir.txt");
// Verificando la operacion -- si el archivo existe o no
if (!entrada)
{
cout<<"No se puede abrir el archivo\n";
exit(1);
}
else
{
/*
do
{
//Lectura de un archivo de texto
entrada >> i;
cout << i << endl; // muestra lo pantalla
} while (entrada);
*/
}
entrada.close();
//Cierre del archivo
cout<<"****** Archivo Procesado ******\n";
return 0;
}
Hay muchas formas de hacer un ciclo iterativo para leer un archivo
de forma secuencial línea por línea hasta el final. Las siguientes
son alternativas:
while (entrada >> i) {
cout << i << endl; // muestra lo pantalla
}
while (!entrada.eof()) {
entrada >> i;
cout << i << endl; // muestra lo pantalla
}
entrada >> i;
while (!entrada.eof()) {
cout << i << endl; // muestra lo pantalla
entrada >> i;
}
Sin embargo, la última alternativa deja de procesar la última
línea. Esto se debe a que cuando se abre un archivo de lectura, un
cursor que forma parte del archivo lógico se ubica en la primera
línea del archivo físico, luego una vez leída la línea el cursor
se ubica en la siguiente. Cuando se ejecuta eof(), este método
determina si dicho cursor esta parado en el carácter de fin de
archivo y se da el valor de verdad en caso positivo, entonces se
sale del ciclo y no se procesa la última línea. Se debe tener
cuidado con la forma en que realiza el ciclo iterativo.
Cuando se tienen archivos de textos estructurados, es decir con
más de un valor por línea y de tamaño fijo el número de campos. Se
puede escribir con el operador << y agregar el separador que se
necesite. Para leer de debe usar el operador >> tantas veces como
valores están separados por un espacio en blanco o fin de línea.
El siguiente código ilustra como crear y leer un archivo de 2
enteros por líneas:
#include <iostream>
#include <fstream>
using namespace std;
int main(){
int numl,i,j;
ofstream salida; // flujo de salida
salida.open("ejemplo-escribir2.txt",ios::out);
if (!salida)
{
cout<<"No se puede abrir el archivo\n";
exit(1);
}
else
{
cout<<"Suministre el total de lineas del archivo: ";
cin>>numl;
for (i=0;i<numl;i++)
{
//Escritura de un archivo de texto
salida << i+1 << " " << numl+i <<"\n";
}
}
salida.close(); //Cierre del archivo
cout<<"****** Archivo de salida Procesado ******\n";
ifstream entrada; // flujo de entrada
entrada.open("ejemplo-escribir2.txt");
if (!entrada)
{
cout<<"No se puede abrir el archivo\n";
exit(1);
}
else
{
do
{
//Lectura de un archivo de texto
entrada >> i >> j;
cout << i << " " << j << endl; // muestra lo pantalla
} while (entrada);
}
entrada.close();
//Cierre del archivo
cout<<"****** Archivo de entrada Procesado ******\n";
return 0;
}
El archivo “ejemplo-escribir2.txt” para un valor numl = 3 es el
siguiente:
1 3
2 4
3 5
De la misma forma puede escribirse y leer un registro (struct de
C++) sobre un archivo de texto, de la siguiente forma:
struct datos{
int ci;
string nombre;
};
. . .
ofstream salida(“ejemplo3.txt”);
datos D;
salida << D.ci << "\t" << D.nombre <<"\n";
salida.close();
. . .
ifstream entrada("ejemplo-escribir3.txt");
datos D1;
entrada >> D1.ci >> D1.nombre;
entrada.close();
Si el campo nombre puede esta formado por más de una palabra
entonces debe usarse getline para leer toda la línea y procesarla
con los métodos de la clase string, pues el operador >> y << no
servirán en este caso.
Estado de las operaciones de E/S sobre archivos
Se recomienda utilizar los siguientes métodos para verificar el
éxito de las operaciones de, apertura, lectura y escritura sobre
archivos:
• good(): produce un 1 si la operación previa fué exitosa.
• eof(): produce un 1 si se encuentra el final del archivo.
• fail(): produce un 1 si ocurre un error.
• bad(): produce un 1 si se realiza una operación inválida.
Ejercicio
1. El departamento de Robótica de una compañía tiene varios
tipos de robots. Para cada robot se tiene un archivo de texto
secuencial (Archivo 1) que contiene los registros de las
órdenes válidas formadas por 2 campos:
a. el campo parte (cabeza, manos, brazos, piernas, pies,
etc.) del robot y
b. el campo movimiento (derecha, izquierda, arriba, abajo,
etc.) de la parte
Este archivo contiene registros ordenados por el campo parte
(puede estar duplicado!). Por otra parte, la interfaz del
robot puede ingresar todas las combinaciones de partes y
movimientos a través del Archivo 2, pero para cada tipo de
robot el Archivo 1 determina sus movimientos válidos. El
Archivo 2, entonces expresa los requerimientos del usuario
para intentar mover el robot y contiene la misma estructura
que el Archivo 1 con la diferencia que contiene movimientos
válidos e inválidos desordenados del robot. Se quiere que
ud., procese los archivos de texto secuencial para que:
a) Implemente un validador: dado como parámetros el Archivo 1
y Archivo 2, diga si el Archivo 2 es válido o inválido
según el Archivo 1
b) Implemente un filtro: dado como parámetros el Archivo 1 y
Archivo 2, genere la partición del Archivo 2 en dos
archivos de salidas: Ordenes Válidas y Ordenes Inválidas,
según el Archivo 1
Por ejemplo:
Archivo 2
PIED
DER
CABEZA ARR
CABEZA DER
BRAZOD DER
MANOI
ARR
PIEI
IZQ
BRAZOI
IZQ
Archivo 1
BRAZOD ABA
BRAZOD ARR
CABEZA ARR
CABEZA DER
CABEZA IZQ
MANOD
IZQ
Archivo1
Archivo2
Archivo 1
Archivo 2
Validador
(a)
Filtro (b)
Inválido
Ordenes Inválidas
PIED
DER
BRAZOD
DER
MANOI
ARR
PIEI
IZQ
BRAZOI
IZQ
Ordenes Válidas
CABEZA
ARR
CABEZA
DER
Elaborado: prof. Hilda Contreras
Revisado: prof. Rafael Rivas y prof. Gilberto Díaz.
Descargar