Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


5.3.2d  basic_ostream

§1  Sinopsis

Esta superclase proporciona los métodos para las operaciones de salida (escritura) en los dispositivos conectados con los filestreams.  En atención a su capacidad para las operaciones de salida ("Output"), a los miembros de esta clase se los denomina de forma genérica ostreams.

Al igual que ocurre en los flujos de entrada, los ostreams también disponen dos grupos de métodos que permiten salidas formateadas y no formateadas. Los primeros se denominan insertores, aunque ambos grupos "insertan" caracteres en el dispositivo de salida a través de la capa de transporte representada por un buffer intermedio ( 5.3.2f).

Los métodos de salida también construyen un objeto-centinela de la clase classbasic_ostream::sentry, para garantizar la inicialización segura; controlar el estado del flujo, y bloquearlo si se trabaja en una aplicación multihebra.

La mecánica de salidas es análoga a la de entradas, con la diferencia de que se dispone de métodos específicos para escritura.  Estos métodos también realizan una invocación a setstate(x) con el argumento x adecuado a la situación presentada (fin de fichero, error, Etc.) si la operación no finaliza con éxito.

§2  Interfaz

template <class charT, class traits = char_traits<charT> >
   class basic_ostream : virtual public basic_ios<charT,traits> {
   public:

//  Tipos heredados de basic_ios:
   typedef charT char_type;
   typedef typename traits::int_type int_type;
   typedef typename traits::pos_type pos_type;
   typedef typename traits::off_type off_type;
   typedef traits traits_type;


//  Constructor / Destructor:
   explicit basic_ostream(basic_streambuf<char_type,traits>* sb);
   virtual ~basic_ostream();


   class sentry;   // clase centinela


//  15 métodos para salidas formateadas (Insertores) ( 5.3.3b)

//  Salidas no formateadas:
   basic_ostream<charT,traits>& put(char_type c); 
   basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
   basic_ostream<charT,traits>& flush();   

//  Posicionamiento del puntero:
   pos_type tellp();   
   basic_ostream<charT,traits>& seekp(pos_type);  
   basic_ostream<charT,traits>& seekp(off_type, ios_base::seekdir);
};


Además de los 15 insertores definidos como función-miembro, se definen varias funciones adicionales; principalmente para salida caracteres. Son funciones-operador operator<<() definidas como funciones externas ( 5.3.3b).

§2.1  Comentario:

Para las salidas no formateadas se dispone de los siguientes métodos:

§3  ostream_type& flush();

La función devuelve una referencia al objeto-flujo (*this). Su efecto es vaciar el almacenamiento intermedio escribiendo todo su contenido en el fichero de salida. En realidad la operación se realiza invocando un método del objeto-buffer, de forma que la invocación

myOstream.flush();

se transforma en:

myOstream.rdbuf()->pubsync();

A fin de garantizar que los datos serán escritos en el dispositivo de salida y prevenir contra caídas intempestivas del sistema, después de transacciones importantes, es aconsejable utilizar este método, especialmente si el fichero va a permanecer abierto [1].

§4  basic_ostream& write(const char_type* s, streamsize n);

La función va tomando caracteres de posiciones sucesivas de la matriz señalada por el puntero s y las inserta en el dispositivo de salida hasta que se alcanza alguna de las condiciones siguientes:

  • Se insertan n caracteres

  • La inserción falla. En cuyo caso se invoca setstate(badbit)

Observe que es responsabilidad del programador garantizar que el valor n concuerda con el tamaño de la matriz s utilizada.

§5  basic_ostream<charT,traits>& put(char_type c);

El efecto es insertar el carácter c en la secuencia de salida. En caso de fallo se sigue el procedimiento habitual.

§6  seekp()

Este método tiene dos formas y su comportamiento es análogo al de seekg ( 5.3.2c) en los flujos de entrada.  En ambos casos se devuelve una referencia al propio objeto (*this) y se desplaza el puntero de la secuencia en el valor definido por pos.  El argumento mode de la segunda forma, permite definir el modo de desplazamiento seekdir ( 5.3.2a).

basic_ostream<charT,traits>& seekp(pos_type& pos);
basic_ostream<charT,traits>& seekp(off_type& pos, ios_base::seekdir mode);

Una vez comprobado que el desplazamiento es posible, verificando que fail 5.3.2b) devuelve false, se realiza una invocación al método correspondiente del buffer intermedio (que controla estos aspectos del flujo), de forma que las invocaciones a estos métodos se transforman en rdbuf()->pubseekpos( pos) y rdbuf()->pubseekoff(pos,mode) respectivamente.

§7   pos_type tellp();

Este método devuelve la posición actual del puntero interno, o -1 en caso de error. La llamada se transforma en la invocación a un método del bufer intermedio: rdbuf()->pubseekoff(0,cur, out).

El ejemplo que sigue utiliza los métodos anteriores para escribir cierto contenido en un fichero

ofstream stream ("newFile.txt", ios::out | ios::trunc);
char* contenido = "AEIOU";
char fin = 'F';


stream.seekp(100, ios::beg);  // desplazar puntero
stream.write(contenido, 5);   // escribir

stream.put(fin);              // escribir
ofstream.flush();             // forzar escritura del buffer

 

stream.seekp(0, ios::end);    // ir al final del fichero
ofstream::pos_type pos = stream.tellp();       // comprobar posición

cout << "Tamaño del fichero: " << pos << endl; // resultado

Salida

Tamaño del fichero: 106

Comentario

El modo de apertura elegido borra cualquier fichero anterior del mismo nombre, así que después de ejecutada la primera sentencia, su tamaño es cero. Observe que la matriz contenido tiene realmente seis caracteres (termina en un carácter nulo 3.2.3f), pero hemos indicado 5 en la cantidad a escribir.

Como el primer seekp() desplaza el inicio de la escritura más allá del final actual, el tamaño aumenta en una cantidad mayor que los caracteres realmente escritos. Como resultado, el contenido del fichero es basura en los 100 primeros caracteres (seguramente trozos de ficheros antiguos ya borrados), seguidos de la secuencia "AEIOUF".

Señalar finalmente que si el programa fuese corto, el vaciado de buffers no sería necesario, se realizaría automáticamente en cuanto se cerrara el fichero, stream.close(), o cuando el objeto stream fuese destruido (al salir de la función actual).

El ejemplo que sigue puede servir como comprobación del supuesto comentado anteriormente ( 5.3.1), al tratar las diferencias entre las operaciones de flujo formateadas y no formateadas.

char* S1 = "AEIO";
float S2 = 12.5;
char* pS2 = (char*) &S2;      // L.3
cout.write(S1, 4);            // L.4
cout.write(pS2, 4);           // L.5
cout << endl;                 // L.6

cout << S1 << S2 << endl;

Salida

AEIO  HA
AEIO12.5

Comentario

Las dos primeras líneas preparan los valores de partida. La salida no-formateada está representada por las líneas 4 y 5, mientras que la versión formateada se realiza en la última sentencia. La sentencia L.6 simplemente inserta una nueva línea y fuerza la escritura del buffer de salida. La hemos colocado para separar ambas salidas.

Los únicos puntos a destacar en el programa son:  comprobar como la clase cout dispone también de métodos para operaciones planas, y el artificio empleado para utilizar el método write() con un float. Como este método espera un puntero a matriz de caracteres, lo definimos en L.3, procurando que señale al principio del almacenamiento de S2 y realizando el modelado necesario para que el compilador no proteste.

  Inicio.


[1]  Al tratar de las salidas formateadas (insertores) veremos que, en estos casos, el forzado de escritura del bufer se realiza mediante manipuladores.