Выбрать главу

  testDate("08%10/2003");

} ///:~

Each string literal in main( ) is passed by reference to testDate( ), which in turn wraps it in an istringstream so we can test the stream extractor we wrote for Date objects. The function testDate( ) also begins to test the inserter, operator<<( ).

Output string streams

To create an output string stream to put data into, you just create an ostringstream object, which manages a dynamically sized character buffer to hold whatever you insert. To get the formatted result as a string object, you call the str( ) member function. Here’s an example:.

//: C04:Ostring.cpp

// 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;

} ///:~

This is similar to the Istring.cpp example earlier that fetched an int and a float. A sample execution follows (the keyboard input is in bold type).

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

integer = 10

float = 20.5

string = the end

You can see that, like the other output streams, you can use the ordinary formatting tools, such as the << operator and endl, to send bytes to the ostringstream. The str( ) function returns a new string object every time you call it so the underlying stringbuf object owned by the string stream is left undisturbed.

In the previous chapter, we presented a program, HTMLStripper.cpp, that removed all HTML tags and special codes from a text file. As promised, here is a more elegant version using string streams.

//: C04:HTMLStripper2.cpp

//{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 "../require.h"

using namespace std;

string& replaceAll(string& context, const string& from,

  const string& to);

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;

  }

} ///:~

In this program we read the entire file into a string by inserting a rdbuf( ) call to the file stream into an ostringstream. Now it’s an easy matter to search for HTML delimiter pairs and erase them without having to worry about crossing line boundaries like we had to with the previous version in Chapter 3.

The following example shows how to use a bidirectional (that is, read/write) string stream.

//: C04:StringSeeking.cpp

// Reads and writes a string stream

//{-bor}

#include <cassert>

#include <sstream>

#include <string>

using namespace std;

int main() {

  string text = "We will sell no wine";

  stringstream ss(text);

  ss.seekp(0, ios::end);

  ss << " before its time.";

  assert(ss.str() ==

    "We will sell no wine before its time.");

  // Change "sell" to "ship"

  ss.seekg(9, ios::beg);

  string word;

  ss >> word;

  assert(word == "ell");

  ss.seekp(9, ios::beg);

  ss << "hip";

  // Change "wine" to "code"

  ss.seekg(16, ios::beg);

  ss >> word;

  assert(word == "wine");

  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.");

} ///:~

As always, to move the put pointer, you call seekp( ), and to reposition the get pointer, you call seekg( ). Even though we didn’t show it with this example, string streams are a little more forgiving than file streams in that you can switch from reading to writing or vice-versa at any time. You don’t need to reposition the get or put pointers or flush the stream. This program also illustrates the overload of str( ) that replaces the stream’s underlying stringbuf with a new string.

Output stream formatting

The goal of the iostreams design is to allow you to easily move and/or format characters. It certainly wouldn’t be useful if you couldn’t do most of the formatting provided by C’s printf( ) family of functions. In this section, you’ll learn all the output formatting functions that are available for iostreams, so you can format your bytes the way you want them.