5.7.2. Streams de cadena de salida

Para crear un stream de cadena de salida, simplemente cree un objeto ostringstream, que maneja un buffer de carácteres dinamicamente dimensionado para guardar cualquier cosas que usted inserte. Para tomar el resultado formateado como un objeto de string, llame a la función miembro setr(). Aqui tiene un ejemplo:

//: C04:Ostring.cpp {RunByHand}
// Illustrates ostringstream.
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main() {
  cout << "type an int, a float and a string: ";
  int i;
  float f;
  cin >> i >> f;
  cin >> ws; // Throw away white space
  string stuff;
  getline(cin, stuff); // Get rest of the line
  ostringstream os;
  os << "integer = " << i << endl;
  os << "float = " << f << endl;
  os << "string = " << stuff << endl;
  string result = os.str();
  cout << result << endl;
} ///:~

Listado 5.12. C04/Ostring.cpp


Esto es similar al ejemplo Istring.cpp anterior que pedía un int y un float. A continueación una simple ejecución (la entrada por teclado está escrita en negrita).

type an int, a float and a string: FIXME:10 20.5 the end
integer = 10
float = 20.5
string = the end

Puede ver que, como otros stream de salida, puede usar las herramientas ordinarias de formateo, como el operador << y endl, para enviar bytes hacia el ostringstream. La función str() devuelve un nuevo objeto string cada vez que usted la llama con lo que el stringbuf contenido permanece inalterado.

En el capítulo previo, presentamos un programa, HTMLStripper.cpp, que borraba todas las etiqietas HTML y los códigos especiales de un fichero de texto. Como prometíamos, aqui está una versión más elegante usando streams de cadena.

//: C04:HTMLStripper2.cpp {RunByHand}
//{L} ../C03/ReplaceAll
// Filter to remove html tags and markers.
#include <cstddef>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include "../C03/ReplaceAll.h"
#include "../require.h"
using namespace std;

string& stripHTMLTags(string& s) throw(runtime_error) {
  size_t leftPos;
  while((leftPos = s.find('<')) != string::npos) {
    size_t rightPos = s.find('>', leftPos+1);
    if(rightPos == string::npos) {
      ostringstream msg;
      msg << "Incomplete HTML tag starting in position "
          << leftPos;
      throw runtime_error(msg.str());
    }
    s.erase(leftPos, rightPos - leftPos + 1);
  }
  // Remove all special HTML characters
  replaceAll(s, "&lt;", "<");
  replaceAll(s, "&gt;", ">");
  replaceAll(s, "&amp;", "&");
  replaceAll(s, "&nbsp;", " ");
  // Etc...
  return s;
}

int main(int argc, char* argv[]) {
  requireArgs(argc, 1,
    "usage: HTMLStripper2 InputFile");
  ifstream in(argv[1]);
  assure(in, argv[1]);
  // Read entire file into string; then strip
  ostringstream ss;
  ss << in.rdbuf();
  try {
    string s = ss.str();
    cout << stripHTMLTags(s) << endl;
    return EXIT_SUCCESS;
  } catch(runtime_error& x) {
    cout << x.what() << endl;
    return EXIT_FAILURE;
  }
} ///:~

Listado 5.13. C04/HTMLStripper2.cpp


En este programa leemos el fichero entero dentro de un string insertando una llamada rdbuf() del stream de fichero al ostringstream. Ahora es fácil buscar parejas de delimitadores HTML y borrarlas sin tener que preocuparnos de límites de líneas como teniamos con la versión previa en el Capítulo 3.

El siguiente ejemplo muestra como usar un stream de cadena bidireccional (esto es, lectura/escritura):

//: C04:StringSeeking.cpp {-bor}{-dmc}
// Reads and writes a string stream.
#include <cassert>
#include <sstream>
#include <string>
using namespace std;

int main() {
  string text = "We will hook no fish";
  stringstream ss(text);
  ss.seekp(0, ios::end);
  ss << " before its time.";
  assert(ss.str() ==
    "We will hook no fish before its time.");
  // Change "hook" to "ship"
  ss.seekg(8, ios::beg);
  string word;
  ss >> word;
  assert(word == "hook");
  ss.seekp(8, ios::beg);
  ss << "ship";
  // Change "fish" to "code"
  ss.seekg(16, ios::beg);
  ss >> word;
  assert(word == "fish");
  ss.seekp(16, ios::beg);
  ss << "code";
  assert(ss.str() ==
    "We will ship no code before its time.");
  ss.str("A horse of a different color.");
  assert(ss.str() == "A horse of a different color.");
} ///:~

Listado 5.14. C04/StringSeeking.cpp


Como siempre para mover el puntero de inserción, usted llama a seekp(), y para reposicionar el fichero de lectura, usted llama a seekg(). Incluso aunque no lo hemos mostrado con este ejemplo, los stream de cadeana son un poco más permisivos que los stream de fichero ya que podemos cambiar de lectura a escritura y viceversa en cualquier momento. No necesita reposicionar el puntero de lectura o de escritura o vaciar el stream. Este progrma también ilustra la sobrecarga de str() que reemplaza el stringbuf contenido en el stream con una nueva cadena.