The Boost C++ Libraries

Filters

Besides devices, Boost.IOStreams also provides filters, which operate in front of devices to filter data read from or written to devices. The following examples use boost::iostreams::filtering_istream and boost::iostreams::filtering_ostream. They replace boost::iostreams::stream, which doesn’t support filters.

Example 34.6. Using boost::iostreams::regex_filter
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/regex.hpp>
#include <boost/regex.hpp>
#include <iostream>

using namespace boost::iostreams;

int main()
{
  char buffer[16];
  array_sink sink{buffer};
  filtering_ostream os;
  os.push(regex_filter{boost::regex{"Bo+st"}, "C++"});
  os.push(sink);
  os << "Boost" << std::flush;
  os.pop();
  std::cout.write(buffer, 3);
}

Example 34.6 uses the device boost::iostreams::array_sink to write data to an array. The data is sent through a filter of type boost::iostreams::regex_filter, which replaces characters. The filter expects a regular expression and a format string. The regular expression describes what to replace. The format string specifies what the characters should be replaced with. The example replaces Boost with C++. The filter will match one or more consecutive instances of the letter o in Boost, but not zero instances.

The filter and the device are connected with the stream boost::iostreams::filtering_ostream. This class provides a member function push(), which the filter and the device are passed to.

The filter(s) must be passed before the device; the order is important. You can pass one or more filters, but once a device has been passed, the stream is complete, and you must not call push() again.

The filter boost::iostreams::regex_filter can’t process data character by character because regular expressions need to look at character groups. That’s why boost::iostreams::regex_filter starts filtering only after a write operation is complete and all data is available. This happens when the device is removed from the stream with the member function pop(). Example 34.6 calls pop() after Boost has been written to the stream. Without the call to pop(), boost::iostreams::regex_filter won’t process any data and won’t forward data to the device.

Please note that you must not use a stream that isn’t connected with a device. However, you can complete a stream if you add a device with push() after a call to pop().

Example 34.6 displays C++.

Example 34.7. Accessing filters in boost::iostreams::filtering_ostream
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/counter.hpp>
#include <iostream>

using namespace boost::iostreams;

int main()
{
  char buffer[16];
  array_sink sink{buffer};
  filtering_ostream os;
  os.push(counter{});
  os.push(sink);
  os << "Boost" << std::flush;
  os.pop();
  counter *c = os.component<counter>(0);
  std::cout << c->characters() << '\n';
  std::cout << c->lines() << '\n';
}

Example 34.7 uses the filter boost::iostreams::counter, which counts characters and lines. This class provides the member functions characters() and lines().

boost::iostreams::filtering_stream provides the member function component() to access a filter. The index of the respective filter must be passed as a parameter. Because component() is a template, the type of the filter must be passed as a template parameter. component() returns a pointer to the filter. It returns 0 if an incorrect filter type is passed as a template parameter.

Example 34.7 writes five characters to the stream. It does not write a newline (\n). Thus, the example displays 5 and 0.

Example 34.8. Writing and reading data compressed with ZLIB
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <vector>
#include <string>
#include <iostream>

using namespace boost::iostreams;

int main()
{
  std::vector<char> v;
  back_insert_device<std::vector<char>> snk{v};
  filtering_ostream os;
  os.push(zlib_compressor{});
  os.push(snk);
  os << "Boost" << std::flush;
  os.pop();

  array_source src{v.data(), v.size()};
  filtering_istream is;
  is.push(zlib_decompressor{});
  is.push(src);
  std::string s;
  is >> s;
  std::cout << s << '\n';
}

Example 34.8 uses the stream boost::iostreams::filtering_istream in addition to boost::iostreams::filtering_ostream. This stream is used when you want to read data with filters. In the example, compressed data is written and read again.

Boost.IOStreams provides several data compression filters. The class boost::iostreams::zlib_compressor compresses data in the ZLIB format. To uncompress data in the ZLIB format, use the class boost::iostreams::zlib_decompressor. These filters are added to the streams using push().

Example 34.8 writes Boost to the vector v in compressed form and to the string s in uncompressed form. The example displays Boost.

Note

Please note that on Windows, the Boost.IOStreams prebuilt library doesn’t support data compression because, by default, the library is built with the macro NO_ZLIB on Windows. You must undefine this macro, define ZLIB_LIBPATH and ZLIB_SOURCE, and rebuild to get ZLIB support on Windows.

Exercise

Create two command line programs which can compress and uncompress a file.