5.7. Iostreams de string

Un stream de cadena funciona directamente en memoria en vez de con ficheros o la salida estándar. Usa las mismas funciones de lectura y formateo que usó con cin y cout para manipular bits en memoria. En ordenadores antiguos, la memoria se refería al núcleo, con lo que este tipo de funcionalidad se llama a menudo formateo en el núcleo.

Los nombres de clases para streams de cadena son una copia de los streams de ficheros. Si usted quiere crear un stream de cadena para extraer carácteres de él, puede crear un istringstream. Si quiere poner carácteres en un stream de cadena, puede crear un ostringstream. Todas las declaraciones para streams de cadena están en la cabecera estándar <sstream>. Como es habitual, hay plantillas de clases dentro de la jerarquia de los iostreams, como se muestra en la siguiente figura:

5.7.1. Streams de cadena de entrada

Para leer de un string usando operaciones de stream, cree un objeto istringstream inicializado con el string. El siguiente programa muestra como usar un objeto istringstream:

//: C04:Istring.cpp
// Input string streams.
#include <cassert>
#include <cmath>  // For fabs()
#include <iostream>
#include <limits> // For epsilon()
#include <sstream>
#include <string>
using namespace std;

int main() {
  istringstream s("47 1.414 This is a test");
  int i;
  double f;
  s >> i >> f; // Whitespace-delimited input
  assert(i == 47);
  double relerr = (fabs(f) - 1.414) / 1.414;
  assert(relerr <= numeric_limits<double>::epsilon());
  string buf2;
  s >> buf2;
  assert(buf2 == "This");
  cout << s.rdbuf(); // " is a test"
} ///:~

Listado 5.10. C04/Istring.cpp


Puede ver que es un acercamiento más flexible y general para transformar cadenas de carácteres para valores con tipo que la librería de funciones del estándar de C, como atof() o atoi(), aunque esta última puede ser más eficaz para las conversiones individuales.

En la expresión s >> i >> f, el primer número se extrae en i, y en el segundo en f. Este no es 'el primer conjunto de carácteres delimitado por espacios en blanco' por que depende del tipo de datos que está siendo extraído. Por ejemplo, si la cadena fuera '1.414 47 This is a test', entonces i tomaría el valor 1 porque la rutina de entrada se pararía en el punto decimal. Entonces f tomaría 0.414. Esto puede ser muy útil i si quiere partir un número de coma flotante entre la parte entera y la decimal. De otra manera parecería un error. El segundo assert() calcula el error relativo entre lo que leemos y lo que esperamos; siempre es mejor hacer esto que comparar la igualdad de números de coma flotante. La constante devuelta por epsilon(), definida en <limits>, representa la epsilon de la máquina para números de doble precisión, el cual es la mejor tolerancia que se puede esperar para satisfacer las comparaciones de double.[16].

Como debe haber supuesto, buf2 no toma el resto del string, simplemente la siguiente palabra delimitada por espacios en blanco. En general, el mejor usar el extractor en iostreams cuando usted conoce exactamente la secuencia de datos en el stream de entrada y los convierte a algún otro tipo que un string de carácteres. No obstante, si quiere extraer el resto del string de una sola vez y enviarlo a otro iostream, puede usar rdbuf() como se muestra.

Para probar el extractor de Date al principio de este capítulo, hemos usado un stream de cadena de entrada con el siguiente programa de prueba:

//: C04:DateIOTest.cpp
//{L} ../C02/Date
#include <iostream>
#include <sstream>
#include "../C02/Date.h"
using namespace std;

void testDate(const string& s) {
  istringstream os(s);
  Date d;
  os >> d;
  if(os)
    cout << d << endl;
  else
    cout << "input error with \"" << s << "\"" << endl;
}

int main() {
  testDate("08-10-2003");
  testDate("8-10-2003");
  testDate("08 - 10 - 2003");
  testDate("A-10-2003");
  testDate("08%10/2003");
} ///:~

Listado 5.11. C04/DateIOTest.cpp


Cada literal de cadena en main() se pasa por referencia a testDate(), que a su vez lo envuelve en un istringstream con lo que podemos probar el extractor de stream que escribimos para los objetos Date. La función testDate() también empieza por probar el insertador, operator<<().



[16] Para más información sobre la epsilon de la máquina y el cómputo de punto flotante en general, vea el artículo de Chuck, "The Standard C Library, Part 3", C/C++ Users Journal, Marzo 1995, disponible en www.freshsources.com/1995006a.htm