Boost库流处理

《C++11/14高级编程:Boost程序库探秘》笔记

boost.iostreams库建立在标准库的IO流框架基础之上,定义了device、source、sink、filter等新的流处理概念,构造了一套全新的易于使用和定制的流处理框架,几乎可以使用流来处理任何数据,例如字符串处理、base64编解码、压缩解压缩、加密解密等。


入门示例

简单例子

使用类似标准流的功能:

#include <boost/iostreams/stream.hpp>       //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp> //预定义的数组设备头文件

namespace io = boost::iostreams;
using namespace io;

int main()
{
    char str[] = "123";
    array_source asrc(str,str+3);   //一个数组源设备,注意构造函数
    stream<array_source> in(asrc);  //搭配源设备定义输入流

    char c1,c2,c3;
    in >> c1 >> c2;                 //使用读取操作从流中读取数据
    assert(c1 == '1' && c2 == '2');

    in.get(c3);                     //可以调用标准流的成员函数
    assert(c3 == '3' && in.good()); //查看流的状态,正常
    assert(in.get(c3).eof());       //流已经结束

    char str2[10];
    array_sink asnk(str2);          //定义接收设备,支持直接传入数组
    stream<array_sink> out(asnk);   //搭配接收设备定义输出流

    out << 'a' << 'b' << 'c';       //使用写入操作符向流写入数据
    assert(str2[0] == 'a' && str2[2] == 'c');

    return 0;
}

这里用到三个iostreams库的组件:array_source、array_sink和stream,用法类似cin/cout。
array_source是iostreams库提供的一个设备,可以把一个字符缓冲区适配成一个源设备,有了源设备,stream就可以用模板参数+构造函数的方法连接到这个设备,形成一个与标准流完全兼容的新的输入流。流连接的设备是源设备,源设备是可读不可写的,所以新流必然是一个输入(只读)流。
array_sink是iostreams库提供的另一个设备,可以把一个字符缓冲区适配成一个接收设备,stream连接array_sink后成为一个输出流,可以写入数据,对流的写入操作最终均会被字符缓冲区接收。

复杂例子

较复杂的例子中增加了过滤流、管道和iostreams库中最重要的io::copy()算法的演示:

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp>         //预定义的数组设备头文件
#include <boost/iostreams/filtering_stream.hpp>     //过滤流头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filter/counter.hpp>       //计数过滤器头文件

namespace io = boost::iostreams;
using namespace io;

int main()
{
    char arr[] = "12345678";
    stream<array_source> in(arr,arr+8); //直接创建输入流

    std::string str;
    filtering_ostream out(          //输出过滤流,需连接sink设备作为链结束
        counter() |                 //预定义的计数过滤器
        io::back_inserter(str));    //适配标准容器作为接收设备

    io::copy(in,out);
    assert(str.size() == 8);
    assert(str == arr);
    assert(out.component<counter>(0)->characters() == 8);//获取计数结果

    return 0;
}

这个例子核心功能在于过滤流使用,filtering_ostream声明了一个用于输出(可写)的过滤流out,与基本流不同的地方在于它不仅可以连接接收设备,也可以连接过滤器,更可以把这些设备连成一个处理链。过滤流out的构造函数中传入了两个设备对象,中间用重载的operator | 连接。“ | “ 被称为管道操作符,与UNIX中的管道操作符用法相似,数据可以从一个设备通过管道流向另一个设备。
过滤流设备链中的第一个设备是counter,是iostreams库预定义的一个过滤器,可以计算通过过滤器的字符数和行数。第二个设备使用了io::back_inserter()函数,它是一个接收设备生成器,可以把一个标准容器适配成一个接收设备。
最后调用io::copy()算法,把数据从一个输入流拷贝到输出流,整个过程,数据就从输入流开始,先流过counter过滤器,然后再流入被适配的string容器,完成流处理过程。
最后一行调用过滤流的成员函数component(),返回流的设备链中的第n个设备,这里是第一个过滤器counter,再调用counter的成员函数characters()获取字符数的统计结果。


设备的特征

设备最基本的特征是它们能够处理的”字符类型“(char type),流中的所有设备协同工作的最低要求是它们处理的必须是同一种”字符类型“。设备的”字符类型“可以使用元函数char_type_of<T>::type获取,类似标准库的std::char_traits<Ch>::char_type。
设备另一个重要特征是它的模式(mode)。在iostreams库中,模式指的是设备的输入输出(读写)访问特征,与C语言的文件模式或新式迭代器的遍历概念比较接近。最常用的有四种模式:输入模式、输出模式、双向模式(可以在两个不相关的字符序列上分别执行读写操作,如std::iostream)、可定位模式(可以在一个字符序列上执行读写操作,但可重定位操作的位置,如std::fstream)。使用mode_of<T>可以获取设备的模式。


设备

设备(device)是iostreams库中最基础的概念,位于头文件<boost/iostreams/concepts.hpp>,是一个可以对序列执行读或写操作的对象,行为取决于它的模式。判断一个类是否是设备可以使用元函数is_device<T>
device是一个模板类,是一个只被用于继承的概念类,它有两个模板参数,参数Mode决定了设备的模式,参数Ch是设备能够处理的字符类型,默认是窄字符char。
device还有两个非常重要的内部类型定义,分别是char_type和category,分别标记了设备的字符类型和分类信息,可以用相应的元函数char_type_of和category_of获取。
device还定义了若干特化,分别表示一些设备的子概念,如:

typedef device<input> source;   //char源设备
typedef device<output> sink;    //char接收设备

iostreams提供了数个预定义的设备:数组设备、标准容器设备、文件设备和空设备等。

数组设备

数组设备位于头文件<boost/iostreams/device/array.hpp>,它提供了对内存字符序列的访问,可以把字符缓冲区适配成seekable(可定位模式)设备。数组设备不负责缓冲区的内存管理,因此它通常搭配静态数组或者std::vector使用。
数组设备有三个:array_source(源设备)、array_sink(接收设备)和array(可定位设备)。这三个数组设备实际上是basic_array_source、basic_array_sink和basic_array的char类型特化,仅在模式上存在不同,最后实现都是同一个类array_adapter。

template<typename Mode,typename Ch>
class array_adapter
{
public:
    typedef Ch                                  char_type;  //字符类型
    typedef std::pair<char_type*,char_type*>    pair_type;
    struct category
        :public Mode,public device_tag,public direct_tag
    {   };

    array_adapter(char_type* begin,char_type* end);     //构造函数
    array_adapter(char_type* begin,std::size_t length);
    array_adapter(char_type (&ar)[N])
        :begin_(ar),end_(ar + N){ }
    //返回pair对象,标记数组设备所能控制的序列范围,即成员变量begin_和end_
    pair_type   input_sequence();
    pair_type   output_sequence();

private:
    char_type*  begin_;         //数组位置指针
    char_type*  end_;
}

用法:array_source特化char类型,,如果要改变定制其他数据类型,不能直接用array_source、array_sink这些处理char的设备,而是使用basic_array_source等模板类自行特化,如:

int buf1[10] = {
   
   1,2,3},buf2[10];
basic_array_source<int> ai(buf1);   //使用int类型的源设备
basic_array<int> ar(buf2);          //使用int类型的可定位设备

stream<basic_array_source<int> > in(ai);
stream<basic_array<int> > out(ar);

io::copy(in,out);
标准容器设备

如果要把标准容器适配成源设备,需要借助boost.range库提供的函数boost::make_iterator_range(),可以把容器直接传递给过滤流从而创建出可用于输入的流。示例:

string str("123");
filtering_istream in(make_iterator_range(str));

vector<char> v(str.begin(),str.end());
filtering_istream in2(make_iterator_range(v));

如果要把标准容器适配成接收设备,iostreams库提供了一个适配类back_insert_device,位于头文件<boost/iostreams/device/back_inserter.hpp>,它调用标准容器的insert()操作,向容器的末尾追加数据,与std::back_insert_iterator很像。为了方便使用,iostreams库提供工厂函数back_inserter(Container& cnt),可以直接返回一个被适配的接收设备:

string str("123");
std::vector<char> v;

io::copy(make_iterator_range(str),io::back_inserter(v));

上述两种方法可以把标准容器用于流处理,但实际上没有符合设备概念的容器设备。iostreams库在示例代码<libs/iostreams/example/container_device.hpp>中提供了三个可用的模板类:container_source、container_sink和container_device,可以把符合随机访问遍历概念的标准容器适配成设备,有需要可以使用,位于名字空间boost::iostreams::example。

文件设备

文件设备位于头文件<boost/iostreams/device/file.hpp>,基于标准库的文件流提供了对文件的访问,用法与标准库的fstream类似。iostreams库提供了三个文件设备:file_source、file_sink和file,分别是源设备、接收设备和可定位设备。
这三个文件设备实际上是basic_file_source、basic_file_sink和basic_file的char类型特化,核心类是basic_file。

template<typename Ch>
class basic_file
{
public:
    typedef Ch      char_type;  //字符类型
    struct          category    //设备的分类
        : public seekable_device_tag,
          public closable_tag,
          public localizable_tag
    {   };
    basic_file(const std::string& path,openmode mode);

    std::streamsize read(char_type* s,std::streamsize n);
    std::streamsize write(const char_type* s,std::streamsize n);
    bool            putback(char_type c);
    std::streampos  seek(stream_offset off,seekdir way,
                        openmode which = in | out);
    void            open(const std::string& path,openmode mode);
    bool            is_open() const;
    void            close();
private:
    struct impl
    {
        impl(const std::string& path,openmode mode) //内部实现类
        {   file_.open(path.c_str(),mode); }
        ~impl(){
   
   if (file_.is_open()) file_.close();}
        std::basic_filebuf file_;       
    };
    shared_ptr<impl> pimpl;
}

basic_file使用shared_ptr和pimp包装了std::basic_filebuf,因此用户无须关心文件的打开关闭问题,shared_ptr自动管理生命周期。

string str("file device");
file_sink fsink("test.txt");

io::copy(
    make_iterator_range(str),
    fsink);

file_source fsrc("test.txt");
io::copy(fsrc,cout);
空设备

空设备位于头文件<boost/iostreams/device/null.hpp>,它们是空对象模式的具体应用,不执行任何动作的设备,iostreams库中提供了两个空设备:null_source和null_sink,分别是源设备和接收设备。
null_source和null_sink实际上是basic_null_source和basic_null_sink的char类型特化,仅是输入输出模式不同,最后实现的都是同一个类basic_null_device:

template<typename Ch,typename Mode>
class basic_null_device{
public:
    typedef Ch      char_type;
    struct          category
        : public Mode,
          public device_tag,
          public closable_tag
    { };
    std::streamsize read(Ch*,std::streamsize){
   
   return 0;}
    std::streamsize write(const Ch*,std::streamsize n){
   
   return n;}
    std::streamsize seek(...){
   
   return -1;}
    void            close(){}
    void            close(BOOST_IOS::openmode){}
}

空设备的所有成员函数都不执行任何操作,不处理数据,用户无法从null_source中获取任何数据,因为read()函数总返回0字节的数据,用户可以向null_sink写入任意数据,但数据会被完全忽略。空设备一般用于某些特定场合,比如完全不关心数据的去向或者禁止读取数据处理。


过滤器

过滤器是一种特殊的设备,它不具有源设备或接收设备的读写功能,只能允许数据流过,但它能够对流过的数据执行任意操作,完成某些特殊的功能。
判断一个类是否是过滤器可以用元函数is_filter<T>。
filter是过滤器的概念类,位于头文件<boost/iostreams/concepts.hpp>。
同device一样,filter也有两个模板参数,参数Mode决定了设备的模式,参数Ch是设备能够处理的字符类型,也可以用相应的元函数char_type_of和category_of获取。
filter还定义了若干特化,分别表示一些设备的子概念,如:

typedef filter<input>       input_filter;   //输入过滤器
typedef filter<output>      output_filter;  //输出过滤器
typedef filter<seekable>    seekable_filter;//可定位过滤器
typedef filter<dual_use>    dual_use_filter;//两用过滤器

iostreams提供了大量预定义的过滤器和辅助用户定制的过滤器,下面先介绍设备链和管道概念,再介绍几个常用的过滤器。

设备链和管道

前面有简单提到过设备链的概念,iostreams库借鉴UNIX中“管道”的概念和形式,数据通过管道从一个设备流向另一个设备,重载operator |,使用“ | ”把多个过滤器连接成链,最后通常以一个设备结束,形成一条设备链。
如果链的终点不是一个设备,那么链就称为“不完整的链”,不能用与IO操作,反之称为“完整的链”。
chain用于管理设备链,位于头文件<boost/iostreams/chain.hpp>,内部使用std::list来保存所有设备的拷贝,成员函数component_type()返回第n个设备的类型信息,而component()则以指针的形式返回该设备。注意,component()的模板参数不能自动推导,必须手工指定。
chain可以一开始构造为空链,也可以传入一个设备或用管道连接的多个设备,随时使用成员函数push()向链的末尾追加设备或流,pop()从链的末尾删去一个设备。
iostreams库的管道功能位于头文件<boost/iostreams/pipeline.hpp>,提供一个辅助类pipeline和重载的“operator |”操作符,使得用户可以用“语法糖”简单地把设备push到链中。
但是被boost.ref库包装后的引用不能使用管道功能,因为经过ref库包装后返回的是reference_wrapper类型,没有“operator |”重载。

计数过滤器

计数过滤器位于头文件<boost/iostreams/filter/counter.hpp>,是一个两用(dual_use)过滤器,也就是说既可用作输入也可以用作输出,它是一个“透明”过滤器,不对流经的数据做任何更改,仅仅统计字符数和行数,类似UNIX工具wc。
counter是一个简单的过滤器,只要把它加入设备链,用io::copy算法让数据流经它,就可以使用成员函数characters()和lines()获取统计数据。

string str("counter\nfilter\n");
filtering_ostream out(counter() | null_sink());

io::copy(boost::make_iterator_range(str),out);

auto pc = out.component<counter>(0);    //获得计数过滤器指针
assert(pc->characters() == 15);
assert(pc->lines() == 2);
换行过滤器

换行过滤器newline_filter是用于解决不同操作系统的文本文件使用的行结束符不同的问题:UNIX使用的是’\n’,Apple II和旧型号Mac(System1直至OS9)使用的是‘\r’,而DOS/Windows使用的则是’\r\n’。
newline_filter位于头文件<boost/iostream/filter/newline.cpp>,是一个两用(dual_use)过滤器。它的构造函数要求指定转换的目标格式常量,在名字空间boost::iostreams::newline里定义了几个换行符常量:

namespace newline {
    
    
    const char CR       = 0x0D;
    const char LF       = 0x0A;

    const int posix     = 1;
    const int mac       = 2;
    const int dos       = 4;
    const int mixed     = 8;
}

使用方法:

string str("abcdef\n" "12345\n" "aochijk\n");//UNIX换行格式
string result;

filtering_ostream out(
    newline_filter(newline::mac) |  //转换到旧式mac格式
    io::back_inserter(result));
io::copy(boost::make_iterator_range(str),out);

<boost/iostream/filter/newline.cpp>还提供了一个用于检查换行符的过滤器——换行符检查器(newline_checker),它不修改文本,在过滤完数据后可以用成员函数获取文本的换行符相关信息,用法与newline_filter基本相同:

filtering_ostream out(
    newline_checker() | io::null_sink());
io::copy(boost::make_iterator_range(str),out);
assert(out.component<newline_checker>(0)->is_posix());
正则表达式过滤器(I)

iostreams库提供了两个正则表达式过滤器,第一个是regex_filter,它在流里搜索匹配的正则表达式,匹配成功则替换文本,位于头文件<boost/iostreams/filter/regex.hpp>,类似于UNIX工具sed。
使用regex_filter必须链接boost.regex库。
basic_regex_filter是正则表达式过滤器的核心类,它使用regex库执行正则表达式匹配,因此在构造时必须传入一个regex对象,构造函数的第二个参数是替换的目标文本,可以直接使用C字符串或者标准字符串形式的简单文本。
basic_regex_filter还有一种构造函数,使用boost.function库,接受任意一个用于格式化的单参函数或函数对象。这个函数接受正则表达式匹配结果的match_results,然后返回处理后的字符串。
regex_filter用法与regex库的regex_replace()功能基本相同,但手法却是流处理的形式。

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp>         //预定义的数组设备头文件
#include <boost/iostreams/filtering_stream.hpp>     //过滤流头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filter/regex.hpp> 
#include <string>
#include <boost/regex.hpp>
#include <iostream>

namespace io = boost::iostreams;
using namespace io;
using namespace std;

int main()
{
    boost::regex reg("a.c");
    string str("abcdef aochijk");
    string result;

    filtering_ostream out(          //输出过滤流,需连接sink设备作为链结束
        regex_filter(reg,"test") |  //正则表达式过滤器
        io::back_inserter(result)); //适配标准容器作为接收设备

    io::copy(
        boost::make_iterator_range(str),
        out);

    std::cout << result << endl;

    return 0;
}
//g++-4.8 boost_iostream.cpp -o boost_io -lboost_regex-mt -lboost_system

为了使用格式化函数,正则表达式必须做出改动,增加子表达式的定义,并编写一个参数为regex_filter::match_type的函数,来处理匹配结果:

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp>         //预定义的数组设备头文件
#include <boost/iostreams/filtering_stream.hpp>     //过滤流头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filter/regex.hpp> 
#include <string>
#include <boost/regex.hpp>
#include <iostream>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    boost::regex reg("a(.)c");
    std::string str("abcdef aochijk");
    std::string result;

    //lambda表达式定义格式化函数,使用第1个子表达式
    auto formatter = [](const regex_filter::match_type& match)
                {
                    return std::string("test-") + match[1] + "-";
                };

    filtering_ostream out(          //输出过滤流,需连接sink设备作为链结束
        regex_filter(reg,formatter) |   //正则表达式过滤器
        io::back_inserter(result)); //适配标准容器作为接收设备

    io::copy(
        boost::make_iterator_range(str),
        out);

    std::cout << result << std::endl;

    return 0;
}
//g++ boost_iostream.cpp -std=c++0x -o boost_io -lboost_regex-mt -lboost_system
//g++4.8的C++11进行编译会编译失败,原因未知
正则表达式过滤器(II)

grep_filter是iostreams库里的另一个正则表达式过滤器,提供类似UNIX工具grep功能,可以抓取流中含有匹配的行,位于头文件<boost/iostreasms/filter/grep.hpp>。
basic_grep_filter是grep_filter的核心类。grep_filter主要功能集中在它的构造函数,它使用正则表达式匹配流中的字符串,并根据options来决定匹配的方式,如果是whole_line,那么保留匹配的行,如果是invert,那么保留不匹配的行,最后提取的行数可以使用成员函数count()获取。

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp>         //预定义的数组设备头文件
#include <boost/iostreams/filtering_stream.hpp>     //过滤流头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filter/grep.hpp>  
#include <string>
#include <boost/regex.hpp>
#include <iostream>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    boost::regex reg("a(.)c");
    std::string str("abcdef\n" "12345\n" "aochijk\n");
    std::string result;

    filtering_ostream out(          //输出过滤流,需连接sink设备作为链结束
        grep_filter(reg) |          //正则表达式过滤器,缺省为whole
        io::back_inserter(result)); //适配标准容器作为接收设备

    io::copy(
        boost::make_iterator_range(str),
        out);

    std::cout << result << std::endl;

    return 0;
}
#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp>         //预定义的数组设备头文件
#include <boost/iostreams/filtering_stream.hpp>     //过滤流头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filter/grep.hpp>  
#include <string>
#include <boost/regex.hpp>
#include <iostream>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    boost::regex reg("a(.)c");
    std::string str("abcdef\n" "12345\n" "aochijk\n");
    std::string result;

    filtering_ostream out(          //输出过滤流,需连接sink设备作为链结束
        grep_filter(reg,regex_constants::match_default,grep::invert) |          //正则表达式过滤器,缺省为whole
        io::back_inserter(result)); //适配标准容器作为接收设备

    io::copy(
        boost::make_iterator_range(str),
        out);

    std::cout << result << std::endl;

    return 0;
}
压缩过滤器

iostreams库支持三种压缩算法,分别是zlib、gzip和bzip2,必须配合相应的zlib、bzip2库才能使用。
例如zlib压缩算法,位于头文件<boost/iostreams/filter/zlib.hpp>

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp>         //预定义的数组设备头文件
#include <boost/iostreams/filtering_stream.hpp>     //过滤流头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filter/zlib.hpp>  
#include <iostream>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    std::string str("12345678j 12345678");
    std::string zip_str,unzip_str;

    filtering_ostream zout(         //输出过滤流,需连接sink设备作为链结束
        zlib_compressor() |         //使用压缩过滤器输出
        io::back_inserter(zip_str));//适配标准容器作为接收设备

    io::copy(
        boost::make_iterator_range(str),
        zout);

    std::cout << zip_str << std::endl;

    filtering_ostream uzout(
        zlib_decompressor() |
        io::back_inserter(unzip_str));
    io::copy(
        boost::make_iterator_range(zip_str),
        uzout);
    std::cout << unzip_str << std::endl;
    assert(unzip_str == str);

    return 0;
}
//g++-4.8 boost_iostream.cpp -o boost_io -lboost_iostreams-mt -lz

设备和过滤器仅是一些孤立的对象,流则是连接它们的纽带,通过流数据才能从一个设备向下一个设备转移,在流转过程中实现数据的处理。

基本流

在iostreams库中基本流有stream和stream_buffer两个模板类,两者的功能和行为很相似,举例讨论stream,位于头文件<boost/iostreams/stream.hpp>
stream是标准库流的子类,具有标准IO流的所有功能,例如get()、read()等操作,但并不像标准库的流那样用名字来区分输入流和输出流,它的方向取决于它关联的设备T——使用源设备就是输入流,使用接收设备就是输出流。
stream可以在构造的时候直接打开设备,也可以稍后调用open()打开设备,它们的函数参数在单参数形式时接受设备的示例,多参数形式时则接受构造设备所需的N个参数,由stream在内部创建设备的实例。如果流已经关联到实际的设备,那么成员函数is_open()会返回true。这时可以使用重载的operator*和operator->来获取流内的设备引用。

过滤流

过滤流是基本流的强化版,不仅能连接设备,更可以连接过滤器和链,因此用途更广泛,功能更强大。
在iostreams库中过滤流有filtering_stream和filtering_streambuf两个模板类,功能和行为相似,举例讨论filtering_stream,位于头文件<boost/iostreams/filtering_stream.hpp>
filtering_stream的接口更接近chain,主要模板参数是Mode,指定了过滤流的模式。过滤流的构造函数可以传入多个设备对象,中间使用管道符(“|”)连接,形成一个设备链,如果终点是一个接收设备,那么它就相当于一个增强的输出流,可以被写入数据,写入数据时执行过滤操作,反之则是一个增强的输入流。需要注意的是,管道操作只针对设备,不能连接流,流对象加入过滤流需要使用成员函数push()。

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp>         //预定义的数组设备头文件
#include <boost/iostreams/filtering_stream.hpp>     //过滤流头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filter/counter.hpp>
#include <iostream>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    char ar[10] = "abcd";
    stream<array_source> sa(ar);        //定义一个字符数组输入流

    counter ct;
    filtering_istream in;           //定义一个空的输入过滤流
    assert(in.empty());

    in.push(ref(ct));               //加入计数过滤器,使用ref包装,链不完整
    assert(!in.is_complete());
    in.push(sa);                    //加入输入流,链完整
    assert(in.is_complete());

    filtering_ostream out(std::cout);
    io::copy(in,out);

    assert(ct.characters() == 10);

    return 0;
}

流处理函数

设备、过滤器和流是iostreams库的三大要件,流虽然把设备和过滤器连接在一起,但真正要让数据流动起来还需要一个外部驱动——流处理函数。
iostreams库提供了数个流处理函数,其中最重要的是io::copy(),它驱动了整个数据流——从源设备或流中读出字符,再写入到接收设备或流中,最后关闭两个设备并返回处理的字符数。
io::copy()位于头文件<boost/iostreams/copy.hpp>,它有四种重载形式,用来应对src和sink是设备或流的情形,前面的代码例子多次使用io::copy()操作流对象,实际上它也可以直接操作设备:

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/array.hpp>         //预定义的数组设备头文件
#include <boost/iostreams/filtering_stream.hpp>     //过滤流头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filter/counter.hpp>
#include <iostream>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    char ar[] = "abcd123";
    stream<array_source> in(ar);    //源设备
    io::copy(in,std::cout);             //从源设备到输出流

    std::string result;
    in.open(ar);                    //再次打开源设备
    io::copy(in,io::back_inserter(result));//从源设备到接收设备

    return 0;
}

除了io::copy()外,iostreams库中还有其他流处理函数,大都位于头文件<boost/iostreams/operations.hpp>中,常用函数包括以下:

  • get(d) :从源设备或流中获取一个字符
  • read(d,s,n):从源设备或流中获取多个字符
  • put(d,c):向接收设备或流中写入一个字符
  • write(d,s,n):向接收设备或流中写入多个字符
  • seek(d,off,way):随机访问设备或流
  • flush(d):刷新设备或整个流,清空缓冲区
  • close(d):关闭设备或流

这些函数主要在编写自定义设备或过滤器时发挥作用


定制设备

定制源设备

编写设备首先需要满足设备的概念,头文件<boost/iostreams/concepts.hpp>定义了若干特化来表示一些设备的子概念,其中就包括为了方便用户扩展而定义的source,声明如下:

typedef device<input> source;   //char源设备

只要自定义的设备public继承source,那么设备就会自动满足源设备的概念,当然,不一定非要使用source作基类(例如iostreams库自己的数组设备和文件设备)。
源设备被用于输入,因此它需要实现一个如下形式的read()成员函数:

//最多读取n个字符到缓冲区s中,然后返回读取的字符数表示读取成功,返回EOF(-1)则表示已经读取完毕
std::streamsize read(char_type* s,std::streamsize n);

示例1:随机数源设备

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/concepts.hpp>
#include <boost/random.hpp>
#include <algorithm>
#include <iostream>
#include <string>
#include <time.h>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

class rand_source:public io::source //定义随机数源设备
{
private:
    std::streamsize count;          //需要产生的随机数数量
public:
    rand_source(int c):
        count(c){}

    //核心函数,实现源设备的流读取功能
    std::streamsize read(char_type* s,std::streamsize n)
    {
        //最多读取n个字符到缓冲区s中,返回读取的字符数
        auto read_count = (std::min)(n,count);

        if(read_count)
            return EOF;

        for(std::streamsize i = 0;i<read_count;++i)
            *s++ = rand()();
        count -= read_count;    //当前剩余的读取数量
        return read_count;      //返回读取的字符数
    }
private:
    //使用变量发生器组合rand48和uniform_smallint
    typedef boost::variate_generator<boost::rand48,boost::uniform_smallint<> > rand_t;

    static rand_t& rand()
    {
        static rand_t r(boost::rand48(time(0)),boost::uniform_smallint<>('0','z'));
        return r;
    }
};

int main()
{
    std::string out;
    io::copy(rand_source(20),       //直接使用源设备
        io::back_inserter(out));    //输出到字符串

    return 0;
}

示例2:改进的随机数源设备
这个例子把流处理的字符类型由char变为unsigned char,因为source是char设备,所以不能从它继承,要从device直接特化,其他逻辑不变:

class rand_source:public io::device<input,unsigned char>
{..};   //实现代码同前

由于流处理设备链上的设备必须使用一致的字符类型,所以需要再定义一个配套的标准容器block,是basic_string的特化:

typedef std::basic_string<unsigned char> block;

使用:

block out;                      //注意不能使用std::string,字符类型不同
io::copy(rand_source(10),       //直接使用源设备
    io::back_inserter(out));    //输出到字符串
定制接收设备

自定义接收设备同样要满足device定义,只不过对于接收设备来说其模式必须为output,它的便捷特化形式是sink:

typedef device<output> sink;    //char接收设备

对于接收设备,需要编写如下形式的write()成员函数:

//从缓冲区s中最多获取n个字符,然后写入某个地方,最后返回写入的字符数
std::streamsize write(const char_type* s,std::streamsize n);

一个简单的接收设备cout_sink实现:

class cout_sink:public io::sink
{
public:
    std::streamsize write(const char_type* s,std::streamsize n)
    {
        std::cout << std::string(s,n) << std::endl;
        return n;
    }
};

定制过滤器

过滤器的实现原理

编写过滤器必须符合filter的概念,位于头文件<boost/iostreams/concepts.hpp>
在编写过滤器前,先了解iostreams库中提供的几个编写过滤器的辅助类,可以让用户更容易地编写过滤器,包括以下几种:

  • aggregate_filter:两用过滤器,一次性接收所有的数据
  • basic_line_filter:两用过滤器,每次提取一行数据
  • basic_stdio_filter:两用过滤器,用于适配标准输入输出流
  • symmetric_filter:两用过滤器,带有内部缓冲区
    前三个用法简单,只要实现纯虚函数do_filter(),在里面编写适当的处理逻辑即可,而最后一个应用比较复杂,不作介绍。
    自定义过滤器支持iostreams的管道符操作,可以让过滤器更好地搭配其他设备和流工作。iostreams库特别提供了一个宏BOOST_IOSTREAMS_PIPABLE,可以只用一行代码就给过滤器增加管道功能:
#define BOOST_IOSTREAMS_PIPABLE(filter,arity)

BOOST_IOSTREAMS_PIPABLE通常用在过滤器定义后,第一个参数是过滤器的名字,第二个参数是过滤器模板参数的数量,如果过滤器不是模板类,那么值应该是0。

aggregate_filter

aggregate_filter位于头文件<boost/iostreams/filter/aggregate.hpp>,使用模板方法模式完成了过滤器所需的大部分读写工作,只需实现过滤的核心功能do_filter()。它有两个参数,第一个src是过滤器接收到的所有字符,可以对这些字符进行任意的处理,然后把处理结果添加到第二个参数dest。
另一个虚函数do_close()在过滤器被关闭时调用,可以用缺省的空实现。
前面regex_filter其实就是利用aggregate_filter实现的。
示范,使用aggregate_filter编写一个十六进制编码的输出过滤器hex_filter,它利用了boost.algorithm库的hex算法:

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/filter/counter.hpp>
#include <boost/iostreams/filter/aggregate.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/algorithm/hex.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

class hex_filter final:public io::aggregate_filter<char>    //定义随机数源设备
{
private:
    typedef io::aggregate_filter<char>  super_type; //简化类型定义
    typedef super_type::vector_type     vector_type;

    virtual void do_filter(
        const vector_type& src,vector_type& dest)
    {
        boost::algorithm::hex(src,std::back_inserter(dest));
    }
};

BOOST_IOSTREAMS_PIPABLE(hex_filter,0);

int main()
{
    char str[] = "313";
    std::string result;

    filtering_ostream out(          //输出流
        hex_filter() |              //hex过滤器
        counter() |                 //统计流过的字符数
        io::back_inserter(result));

    io::copy(array_source(str,str + 3),out);

    assert(result.size() == 6);             //hex编码后长度变为两倍
    assert(out.component<counter>(1)->characters() == 6);

    return 0;
}
basic_line_filter

basic_line_filter位于头文件<boost/iostreams/filter/line.hpp>,用法和aggregate_filter相似,但只过滤一行字符,由纯虚函数do_filter()处理这行字符,再返回处理后的新行。
为了方便使用basic_line_filter提供line_filter和wline_filter两个特化:

typedef basic_line_filter<char>     line_filter;
typedef_basic_line_filter<wchar_t>  wline_filter;
#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/filter/counter.hpp>
#include <boost/iostreams/filter/line.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

class bracket_line_filter final:public io::basic_line_filter<char>  //定义随机数源设备
{
private:
    typedef io::basic_line_filter<char>         super_type; //简化类型定义
    typedef typename super_type::string_type    string_type;

    virtual string_type do_filter(const string_type& line)
    {
        return "<" + line + ">";    //简单处理返回
    }
};

BOOST_IOSTREAMS_PIPABLE(bracket_line_filter,0);

int main()
{
    std::string str("The\n" "Phantom\n" "Pain");

    filtering_istream in(           //输入流
        bracket_line_filter() |
        boost::make_iterator_range(str));

    io::copy(in,std::cout);

    return 0;
}
手工打造过滤器

手工编写过滤器,只要把原来辅助类实现的char_type、category、read()、write()、close()等过滤器必备要素自己写出来即可。
对于多字符过滤器来说,我们需要实现如下形式的读写函数:

template<typename Source>
std::streamsize read(Source& src,char_type* s,std::streamsize n)
{
    //从src读取字符处理,处理后最多写入n个字符到缓冲区s,返回读取的字符数或者EOF
}

template<typename Sink>
std::streamsize write(Sink& dest,const char_type* s,std::streamsize n)
{
    //从缓冲区s最多读取n个字符处理,将处理后的字符写到des,返回已处理的字符数
}

示范,使用boost.uuid库的SHA1工具类实现一个计算SHA1摘要的输出过滤器:

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/filter/counter.hpp>
#include <boost/iostreams/filter/aggregate.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/uuid/detail/sha1.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

class sha1_filter final //不是模板类,也不使用任何的概念类
{
public:
    typedef char char_type;     //字符类型定义
    struct category:            //过滤器分类定义
        output,                 //输出模式
        filter_tag,             //过滤器设备
        multichar_tag,          //处理多字符
        closable_tag            //可关闭
    { };

private:
    boost::uuids::detail::sha1 sha;
public:
    template<typename Sink>     //输出模式需要实现write()函数
    std::streamsize write(Sink&,const char_type* s,std::streamsize n)
    {
        sha.process_bytes(s,n); //处理所有输入字符序列
        return n;               //返回处理的字符数,但暂不输出摘要结果
    }

    template<typename Sink>
    void close(Sink& snk)       //过滤器关闭时给出摘要结果
    {
        unsigned int digest[5];
        sha.get_digest(digest);

        char_type* p = reinterpret_cast<char_type*>(digest);
        io::write(snk,p,sizeof(digest)/sizeof(char_type));  //写入接收设备
    }
};

BOOST_IOSTREAMS_PIPABLE(sha1_filter,0);

int main()
{
    char str[] = "The Evil Within";
    std::string result;

    filtering_ostream out(          //输出流
        sha1_filter() |             //sha1过滤器
        io::back_inserter(result));

    io::write(out,str,3);           //使用write()函数向流写入数据
    assert(result.empty());         //没有输出结果

    io::write(out,str + 3,3);       //继续使用write()函数向流写入数据
    assert(result.empty());         //没有输出结果

    io::close(out);
    assert(result.size() == 20);    //得到摘要结果

    return 0;
}

这里没有使用io::copy()函数,因为io::copy()不仅拷贝了流数据还会自动关闭流,故copy后无法再使用流,而write()函数不会关闭流,所以可以多次写入数据,最后手动关闭流来获取结果。
sha1_filter也可以使用另一个概念类multichar_output_filter简化代码,它仅定义了char_type和category。

实现不关闭设备的copy算法
对于sha1_filter这样的可多段输入的过滤器来说不能使用io::copy()算法,write()用起来显然没有io::copy()方便,所以可以仿照写一个不自动关闭设备的copy版本copy_no_close()函数。

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/copy.hpp>                 //io::copy算法头文件
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

template<typename Source,typename Sink>
std::streamsize copy_no_close_impl(Source src,Sink snk,std::streamsize buffer_size)
{
    return io::detail::copy_operation<Source,Sink>(
        src,snk,buffer_size)();         //调用copy操作,不关闭设备
}

template<typename Source,typename Sink>
std::streamsize copy_no_close(const Source& src,Sink& snk,
    std::streamsize buffer_size = io::default_device_buffer_size
    BOOST_IOSTREAMS_ENABLE_IF_STREAM(Source)
    BOOST_IOSTREAMS_ENABLE_IF_STREAM(Sink))
{
    return copy_no_close_impl(io::detail::wrap(src),io::detail::wrap(snk),buffer_size);
}

int main()
{
    char str[] = "The Evil Within";
    stream<array_source> in(str,str+15);

    std::string result;
    filtering_ostream out(          //输出流
        io::back_inserter(result));

    copy_no_close(in,out);
    assert(in.is_open());
    io::close(in);
    io::close(out);

    return 0;
}

组合设备

iostreams库提供了一些模板类和函数,可以把简单易实现的设备或过滤器组合起来,形成可用的新设备

combine

combination模板类可以“并联”组合一对源/接收设备或者输入/输出过滤器,形成一个新的设备或者过滤器。新设备是一个可在两个独立字符序列上工作的双向设备,使用前者输入的同时使用后者输出。iostreams库提供工厂函数combine()来简化类的构造,位于头文件<boost/iostreams/combine.hpp>

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/combine.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    char src[] = "12345678";    //输入字符序列
    std::string result;         //输出字符序列

    typedef io::combination<array_source,           //组合设备类型定义
        back_insert_device<std::string> > bi_dev;

    assert(is_device<bi_dev>::value);               //检查是否是设备
    assert((is_same<                                //检查是否是双向模式
        mode_of<bi_dev>::type,bidirectional>::value));

    bi_dev dev = io::combine(       //使用函数生成组合设备
        array_source(src),
        io::back_inserter(result));

    io::write(dev,src,2);           //向输出序列写入数据
    assert(result.size() == 2);

    return 0;
}
compose

compose是另一种组合设备或过滤器的工具,它把两个设备“串联”起来,数据顺序(输出模式)或逆序(输入模式)流过新的设备,有些类似管道符作用,位于头文件<boost/iostreams/compose.hpp>
compose提供的模板类是composite,它的第一个模板参数是过滤器,第二模板参数是过滤器、设备或流,类似std::pair可以用first()和second()获取它组合的对象,工厂函数compose()用来自动推导参数生成新设备对象。

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/compose.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/regex.hpp>
#include <boost/regex.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    std::string str("abcdef aochijk");  //输入字符串

    io::copy(
        boost::make_iterator_range(str),
        compose(regex_filter(regex("a.c"),"test"),//正则表达式过滤器
            std::cout)  //标准输出流
        );

    return 0;
}

// g++-4.8 -std=c++11 boost_iostream.cpp -o boost_io -lboost_regex -lboost_system
// 重新安装了boost库后使用g++-4.8的C++11编译成功
invert

invert利用compose的功能,适配一个过滤器并反转它的模式——也就是把输入过滤器变成输出过滤器或者相反,位于头文件<boost/iostreams/invert.hpp>
invert使用模板类inverse来实现模式的反转,元计算出被适配过滤器的模式,然后转变为相反的模式,内部定义一个source或者sink设备,用compose把过滤器和设备组合起来,再完成读写。工厂函数invert()用来自动推导参数生成反转后的新设备对象。
虽然invert只需要一个模板参数,用法简单,但是实际使用会受到限制,比如不能用于两用(dual-use)过滤器,因为两用过滤器本身就支持输入输出,使用invert会导致编译错误(无法反转模式),另外,如前面实现的sha1_filter那样在关闭时有读写操作的过滤器也不能使用invert,因为模板类inverse内部compose的设备只是一个临时对象,仅在读写操作有效。
一个简单的输出过滤器null_filter:

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/invert.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

class null_filter final
{
public:
    typedef char char_type;
    struct category:
        output,
        filter_tag,
        multichar_tag,
        closable_tag
    {   };
public:
    template<typename Sink>
    std::streamsize write(Sink& snk,const char_type* s,std::streamsize n)
    {
        io::write(snk,s,n);
        return n;
    }

    template<typename Sink>
    void close(Sink& snk){}
};

BOOST_IOSTREAMS_PIPABLE(null_filter,0); //支持管道符操作
BOOST_IOSTREAMS_PIPABLE(inverse,1);     //为inverse添加管道符操作

int main()
{
    char str[] = "Ground Zero";
    std::string result;

    filtering_istream in(
        invert(null_filter()) |
        array_source(str));

    io::copy(in,io::back_inserter(result));

    return 0;
}
restrict

restrict是用于为被适配的设备或过滤器添加一个约束,限定只能访问一个子区间,位于头文件<boost/iostreams/restrict.hpp>
因为restrict在C99标准中是一个关键字(在C++中不是),为了保持兼容,它提供了另外一个名字slice,位于头文件<boost/iostreams/slice.hpp>
模板类restriction实现访问限制,关键在于它的构造函数,参数off指定偏移位置,len指定偏移长度,限定只能访问[start + off,start + off + len]的区间,有点类似C的文件操作函数fseek。工厂函数restrict()或slice()用来自动推导参数生成约束后的新设备对象。

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/restrict.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/counter.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

BOOST_IOSTREAMS_PIPABLE(restriction,1);

int main()
{
    char str[] = "1234";
    std::string result;

    filtering_istream in(
        restrict(counter(),1,2) |
        array_source(str));

    io::copy(in,io::back_inserter(result));
    assert(result == "23");
    return 0;
}

restrict最常见用于文件流的访问,指定读写文件的某个特定位置,比调用标准库的seekg()/seekp()方便。

tee

tee工具提供分离流输出的功能,它可以建立流的分支,把上游的数据同时发给另外一个接收设备,配合compose可以实现对一个序列同时执行两种或两种以上不同的流处理,它位于头文件<boost/iostreams/tee.hpp>
示例使用compose创建两个接收设备,并用tee分流,这样数据同时输出到标准流和标准容器:

#include <boost/iostreams/stream.hpp>               //基本流处理所需的头文件
#include <boost/iostreams/device/back_inserter.hpp> //接收设备适配头文件
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/compose.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/regex.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;
using namespace io;
using namespace boost;

int main()
{
    std::string str("abcdef aochijk");
    std::string result;

    filtering_ostream out(          //输出流
        tee(
            compose(regex_filter(regex("a.c"),"test"),std::cout),
            compose(zlib_compressor(),io::back_inserter(result))
            )
        );

    io::copy(boost::make_iterator_range(str),out);
    return 0;
}
//g++-4.8 -std=c++11 boost_iostream.cpp -o boost_io -lboost_regex -lboost_iostreams -lboost_system -lz
//和之前只使用regex的那次编译不一样,必须加上-lboost_iostreams,否则编译失败

与迭代器的比较

流处理与迭代器对数据序列的处理方式非常相似:

  • 都使用copy算法,遍历一个源序列,然后把处理过的数据写入另一个序列
  • 流的模式与迭代器的分类很接近,也可以分为可读的输入模式和可写的输出模式
  • 迭代器可以嵌套组合提供复杂的功能,而流则可以用设备链的形式过滤处理数据
  • 迭代器适配为区间的概念后,也可以像流一样使用过滤器来处理数据

流处理与迭代器之间的差异也很明显:

  • 迭代器基于迭代器设计模式,不使用虚函数,通过改写迭代器的核心操作来完成对数据的处理;iostreams则基于已确定的标准流框架,使用流处理思想,并使用设备、过滤器等概念来处理数据。
  • 迭代器是泛型的,可以处理任何类型的数据;iostreams虽然也是泛型,但更侧重于处理字符类型
  • 迭代器通常用于处理内存中的数据,而iostreams则还可以处理文件、socket等更广范围的数据
  • 迭代器虽然也可以组合使用,但它只能在编译期确定,显然没有流的设备链可以任意添加拆卸的方式灵活

猜你喜欢

转载自blog.csdn.net/zuolj/article/details/82057097
今日推荐