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.
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++
.
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
.
#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
.
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.
Create two command line programs which can compress and uncompress a file.