Flujos

Programación en C++. Computación. Recursos. Operadores. Ostream. Istream

  • Enviado por: María
  • Idioma: castellano
  • País: España España
  • 18 páginas
publicidad
publicidad

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++