Ingeniero en Informática
Flujos
FLUJOS
Los flujos se plantean como una solución a la falta de recursos de I/O de C. Los lenguajes en general tienen implementados recursos de I/O para manejar unos pocos tipos de datos. Con los flujos C++ provee un recurso para manejar I/O para tipos definidos por el usuario de manera fácil, flexible y eficiente
Flujos estándares :
archivo de cabecera : iostream.h
cin : entrada estándar
cout : salida estándar
cerr : salida de error estándar
Operadores :
<< : “poner en”
>> : “extraer de”
En C : printf(“un entero %d, un real %f, entero, real);
scanf(“%d%f%c”, &entero, &real, &carácter);
En C++ :
cout<< “Un entero”<<entero<< “, un real” <<real;
cin >> entero>> real >> carácter;
No es necesario especificar el formato, la sobrecarga de operadores permite leer en cualquier formato. Además, en la entrada de datos no es necesario darle el operador “dirección de” &.
La entrada y salida de datos con los operadores >> y << se hará siempre y cuando estos operadores estén definidos para el tipo especificado. El programador tiene la capacidad de definir los operadores para un nuevo tipo.
Los operadores << y >> se eligieron por tener baja prioridad para permitir, por ejemplo, incluir expresiones aritméticas sin utilizar paréntesis
cout<< “a*b+c2” << a*b+c2 << `\n';
Además de utilizar la sobrecarga de operadores los flujos tienen definida una jerarquía de clase sobre la cual están soportados :
IOS (superclase)
(estados del flujo, formato, manipuladores,…)
ISTREAM OSTREAM
(sobrecarga >>) (sobrecarga <<)
IFSTREAM OFSTREAM
(Para flujos en archivos)
La clase ostream por dentro :
class ostream: public virtual ios {
//…
public :
ostream& operator<<(const char*); //cadenas
ostream& operator<<(char);
ostream& operator<<(short i)
{ return *this<< int(i); }
ostream& operator<< (int);
…
…
};
la operación : cout << “x =” << x; // x es entero
se interpreta como :
(cout.operator<<(“x =”)).operator<<(x);
esto implica que lo que se imprime se hace de izquierda a derecha
Sobrecarga de operador para clase complejo :
ostream& operator<<(ostream& s, complejo z)
{
return s<< “(“ << real(z) <<”,”<< imag(z) << “)”;
}
main()
{
complejo x(1,2);
cout << “x =” << x,'\n';
} // Salida x =(1,2)
Notar que para definir una salida para un tipo definido por el usuario no es necesario modificar la clase ostream ni acceder a la estructura de datos mantenida por la clase
Entradas
La clase istream es similar a la ostream :
class istream: public virtual ios {
//…
public :
istream& operator>>(char*); //cadenas
istream& operator>>(char &);
istream& operator>> (short & );
istream& operator>> (int & );
…
};
Una función se define de la siguiente manera :
istream& istream::operator>>(T& varT)
{
// pasar por alto los espacios en blanco
// leer de algún modo T (tipo) y colocarlo en vart
return *this;}
Como se puede observar los argumentos para las entradas son en general punteros
Funciones útiles para manejar entradas que se encuentran en <ctype.h> :
int isalpha(char) // `a'..'z' `A'..'Z'
int isupper(char) // `A'..'Z'
int islower(char) // `a'..'z'
int isdigit(char) // `0'..'9'
int isxdigit(char) // `0'..'9' `a'..'f' `A'..'F'
int isspace(char) // ` ` `\t' <CR> <LF> nueva página
int iscntrl(char) //carácter de control ASCII 0..31-127
int ispunct(char) // signos puntuación, ninguno anterior
int isascii(char c) { return 0<=c && c<=127}
Estados de flujos
Todo flujo (istream u ostream) tiene un estado asociado. Los estados se pueden examinar con operaciones sobre la clase ios :
class ios {
…
public :
int eof() const; // se detecta el fin de archivo;
int fail() const; // fracasará la siguiente operación
int bad() const; // el flujo está corrompido
int good() const; // la siguiente operación podría tener
// éxito
};
Los valores de los estados también están definidos en la clase ios :
class ios {
// …
public :
enum io-state {
goodbit=0; eofbit=1, failbit=2; badbit=4;};
//…
};
switch (cin.rstate()) {
case ios::goodbit :
// la última operación con cin tuvo éxito
break;
case ios::eofbit :
// final de archivo
break;
case ios::failbit :
// faquí se tuvo un error
break;
case ios::badbit :
// quizás se perdieron caracteres de cin
break;
};
while (cin>>z) cout<<z << `\n';
En el caso del while siempre se comprueba si la operación tiene éxito (estado good)
Entradas para tipos definidos por el usuario
Se pueden definir entradas para tipos definidos por el usuario, pero en este caso al entrada se debe hacer por referencia :
istream& operator<<(istream& s, complejo &a)
{
double re=0, im=0;
char c=0;
s>>c;
if( c== `(`) {
s>>re>>c;
if (c ==',') s >> im >>c;
if(c != `)') s.clear(ios::badbit); // fijar estado
else {
s.putback(c)
s >> re;
}
if (s) a = complejo(re,im);
return s;
}
Formatos
ios es quien controla las operaciones de entrada salida y quien tiene el buffer para estas operaciones, también es quien controla el formato de inserción y extracción de datos.
class ios {
//…
public:
…
int width(int w); // fijar la anchura del campo
int width() const;
char fill(char); // fijar carácter para relleno
char fill() const; // devolver carácter para relleno
…
int precision(int); //fijar precision numeros flotantes
int precision() const;
int rdstate() const; // estados de flujo
…
}
La función width() especifica el número mínimo de caracteres que se empleará para la siguiente operación de salida numérica o de cadena.
cout.width(4);
cout << `(` << 12 << `)'; // Salida ( 12)
width() especifica el número mínimo de caracteres si se especifican más se imprimirán todos:
cout.width(4);
cout << `(` << 121212 << “)\n”;// Salida (121212)
La filosofía de C++ es que es preferible permitir imprimir con un formato desbordado, desprolijo pero que se entienda.
Si queremos especificar un carácter de relleno :
cout width(4);
cout fill(`#');
cout << `(` << “ab” << `)'; // Salida (##ab)
Carácter de relleno por omisión : espacio
Tamaño de campo por omisión : 0 tantos caracteres como sean necesarios. Para restablecer el tamaño de campo por omisión :
cout.width(0); // tantos caracteres como se necesiten
Una llamada de width() solo afecta la salida siguiente :
cout.width(4);
cout.fill(`#');
cout << `(` << 12 << “) , (“ << 12 << “) \n”;
Salida : (##12), (12) no (##12)(##12)
Estado de formato
ios tiene un estado de formato especificado por las funciones flags y setf(), que sirven para prender y apagar las banderas de especificación de formato
class ios {
public:
// banderas para controlar el formato :
enum {
skipws=01,// pasar por alto el espacio en blanco en
// las entradas
//AJUSTE DE CAMPO :
left=02, // relleno despues del valor
right=04, // relleno antes del valor
internal= 010, // relleno entre el signo y el valor
//BASE ENTERA :
dec=020, oct=040, hex=0100,
showbase=0200, // mostrar base entera
showpoint=0400, // imprimir ceros a la derecha
uppercase=01000, // `E', `X' en lugar de `e', `x'
showpos=02000, // `+' explícito para enteros positivos
//ESPECIFICACION PARA PUNTOS FLOTANTES:
scientific=04000, // .dddddd Edd
fixed=010000 // dddd.dd
//
Para especificar un conjunto de opciones :
const int mis_opciones_de_io = ios::left/ios::oct/ios::showpoint/ios::fixed/;
se instalan con una sola opción :
cout.flags(mis_operaciones_de_io);
Una vez encendida una bandera se conserva hasta que se apaga nuevamente.
Cuando la opción a especificar tiene más de un bit o un conjunto de bits es que setf reciba un segundo seudoargumento indicando la opción además del valor :
cout.setf(ios::dec, ios::basefield);//decimal
cout << 1234 << ` `; // por omisión decimal
cout.setf(ios::oct, ios::basefield); //octal
cout << 1234 << ` `;
cout.setf(ios::hex, ios::basefield); //hexadecimal
cout << 1234 << ` `;
1234 2322 4d2
Para ajustar el campo de caracteres tenemos las siguientes opciones :
cout.setf(ios::right, ios::adjustfield);// justificación derecha
cout.width(4);
cout << `(` << -12 << “) \n”;
cout.width(4);
cout.setf(ios::left, ios::adjustfield); // a la izquierda
cout << `(` << -12 << “) \n”;
cout.width(4);
cout.setf(ios::internal, ios::adjustfield); // interna
cout << `(` << -12 << “) \n”;
Salida :
( -12)
(-12 )
(- 12)
Salidas punto flotante
cout << 1234.56789 << `\n';
cout.setf(ios::scientific, ios::floatfield); // notación científica
cout << 1234.56789 << `\n';
cout.setf(ios::fixed, ios::floatfield);
cout << 1234.56789 << `\n';
Salida :
1234.57
1.2345678e+03
1234.567890
Si se quiere especificar la cantidad de dígitos :
cout.precision(n); // valor por omisión es n=6
La llamada a precisión afecta a todas las salidas de punto flotante hasta la siguiente llamada a precisión :
cout.precision(8);
cout << 1234.56789 << `\n';
cout.precision(4);
cout << 1234.56789 << `\n';
Salida :
1234.5679
1235
Los valores se redondean en lugar de truncarse
Para evitar el empleo de prendido y apagado de banderas existen manipuladores estándares para istream y ostream que pueden ser invocados directamente en las entradas (cin) y salidas (cout) estándares
Estos manipuladores son :
// manipuladores simples. Se debe incluir <iomanip.h>
ios& oct(ios&);
ios& dec(ios&);
ios& hex(ios&);
ostream& endl(ostream&); // agregar `\n' y vaciar
ostream& ends(ostream&); // agregar `\0' y vaciar
ostream& flush(ostream&); // vaciar el flujo
istream& ws(istream&); // eliminar espacioblanco
// Manipuladores que reciben argumentos :
SMANIP<int> setbase(int b); //fijar base
SMANIP<int> setfill(int f); //fijar relleno
SMANIP<int> setprecision(int p); //fijar precisión
SMANIP<int> setw(int w); //fijar anchura
SMANIP<long> resetiosflags(long b); //restablece banderas
SMANIP<long> setiosflags(long b); //establece banderas
Se emplean de la siguiente manera :
cout << 1234 << ` ` << hex << 1234 << oct << 1234 << endl;
cout << setw(4) << setfill(`#') << `(` << 12<< “)\n”;
Salida : 1234 4d2 2322
(##12)
Un programador puede crear sus propios manipuladores
Flujos en archivos
Archivo de cabecera : fstream.h (que incluye a iostream.h)
Como ya vimos ifstream y ofstream se derivan de istream y ostream y heredan las operaciones de extracción e inserción respectivamente.
#include <fstream.h>
main()
{
char ch;
ifstream entrada (“a:\\entrada.in”,ios::in);
if(!entrada)
cerr<<“Incapaz de abrir `entrada' para entrada”;
ofstream salida (“a:\\salida.out”,ios::out);
if(!salida)
cerr<<“Incapaz de abrir `salida' para salida”;
while(salida && entrada.get(ch)) //mientras not EOF
// sigue extrayendo caracteres y los pone en salida
salida.put(ch);
entrada.close();
salida.close()
return(0);
}
A veces es mejor pasar el archivo de entrada y de salida como argumentos de entrada al programa :
#include <fstream.h>
main(int argc, char* argv[])
{
if(argc !=3) cerr<<”número de argumentos erroneo”
ifstream entrada (argv[1]);
if(!entrada)
cerr<<“Incapaz de abrir `entrada' para entrada”;
ofstream salida (argv[2]);
if(!salida)
cerr<<“Incapaz de abrir `salida' para salida”;
while(salida && entrada.get(ch)) //mientras not EOF
// sigue extrayendo caracteres y los pone en salida
salida.put(ch);
entrada.close();
salida.close()
return(0);
}
Otro ejemplo :
ifstream mi_arch;
…
mi_arch.open(“entrada.dat”);
…
mi_arch.close();
mi_arch.open(“parametros.dat”);
…
mi_arch.close();
Modos de operación de los flujos
Para modificar el modo en que se abre y/o se utiliza un archivo se emplea el segundo argumento en la construcción del flujo de un archivo:
Modo de Bit | Acción |
ios::in | abre para lectura |
ios::out | abre para escritura |
ios::ate | se posiciona en EOF después de crear el archivo |
ios::app | todas las escrituras van al final archivo |
ios::trunc | si el archivo existe lo corta |
ios::nocreate | si el archivo no existe no lo abre |
ios::noreplace | si el archivo existe no lo abre |
ios::binary | abre archivo en modo binario |
Un archivo se puede abrir tanto para entrada como para salida :
fstream diccionario(“concordancia”, ios::in|ios::out);
Posicionamiento directo
Si visitamos la clase istream y ostream tenemos algunas funciones para posicionar los flujos directamente :
class ostream : public virtual ios {
…
public:
ostream& flush(); //envía contenido buffer a la salida
…
ostream& seekp(streampos);
ostream& seekp(streamoff, seek_dir);
streampos tellp();
…
};
class istream : public virtual ios {
…
public:
int peek();
istream& putback(char c);
…
istream& seekg(streampos);
istream& seekg(streamoff, seek_dir);
streampos tellg();
…
};
seekp y seekg se sitúan en una dirección absoluta dentro del archivo el sufijo p indica poner (put) y el g extraer (get)
streampos representa una posición de carácter dentro del archivo
streamoff representa un desplazamiento a partir de un punto indicado en seek_dir
Valores de seek_dir :
ios::beg desde el principio
ios::cur desde posición actual
ios::end desde el final del archivo
peek : permite determinar cual es el siguiente carácter a leer sin afectar el resultado de la lectura siguiente
putback : permite colocar un carácter no deseado de vuelta en el flujo para leerlo en otra ocasión
Ejemplos :
fstream io(“revision.dat”, ios::in|ios::app);
streampos pos_actual = io.tellp(); // incializa la posición
// actual
io<<object1<<object2<<object3; // se escriben tres objetos
io.seekp(pos_actual); // se reposiciona el puntero en
// pos_actual (principio archivo)
io.seekp(sizeof(OBJECT), ios::cur); // se pasa por alto
// object1
io <<obj2_nuevo; //se graba sobre object2 obj2_nuevo
io.seekg(5,ios::cur); // se posiciona el puntero del archivo
// (get_file) cinco bytes desde la posición actual
io.seekg(-7,ios::end); // siete bytes atrás desde la pos. actual
Universidad Tecnológica Nacional - Santa Fe - Departamento Sistemas -
Curso : Desarrollos de Programación en C++
Descargar
Enviado por: | María |
Idioma: | castellano |
País: | España |