2.1. 概要: InputFilters, OutputFilters 以及 Filter Helpers
Filters 可以用来修改字符序列。例如,你可以通过一个filter将某一个字符全部替换成另外一个字符,将所有字母字符转换为小写格式,或者加密一个文档。有时filter只是充当observer,例如你可以使用一个filter来计算指定单词出现的次数。
InputFilters和OutputFilters
Iostreams库支持两种简单类型的 Filters InputFilters和OutputFilters。InputFilters代表一种"pull"过滤模型:未过滤的数据以source的形式提供出来,Filter将会产生一定量字符的过滤序列。过滤后的序列是以递增的形式产生的,也就是为了过滤一个指定的字符序列,Filter必须被调用多次。OutputFilters代表一种"push"过滤模型:一个未过滤的字符序列和一个Sink需要被提供,另外Filter将会过滤这些字符,并将他们写入Sink。与InputFilters类似,OutputFilters以递增的方式处理数据。
最简单的InputFilters和OutputFilters一次处理一个字符。这种类型的Filter很容易写,但是比那些一次处理多个字符的Filters效率不佳。一次处理多个字符的Filters被称为Multi-Character filters。
Filter Helpers
Iostreams库提供了许多工具以便更容易使用Filter
* aggregate_filter 允许程序员定义一种Filter,它从一个std::vector中读取未过滤数据,并将过滤后的数据写入到另一个std::vector中。
* stdio_filter 允许程序员定义一种Filter,它从标准输入中读取未过滤数据,并将过滤后的数据写入到标准输出中。
* symmetric_filter 允许程序员定义一种Filter,它从数组中读取未过滤数据,并将过滤后的数据写入到另一个数组中。
* finite_state_filter 允许程序员将Filter定义为一个有穷状态机。它目前还不是这个库的正式组成部分。
选择一种 Filter 概念
假定你想利用一个 Filter来执行给定的过滤任务。你将如何决定是使用InputFilter还是OutputFilter,或者使用Filter Helpers?上面提到的两个Filter helpers,aggregate_filter 和 stdio_filter, 具有很高的内存使用,并且只能处理以一个清楚界定结尾的字符串。他们允许过滤算法以一种很简单的方式表达出来,但同时也提供了对过滤的一种很好的介绍。第三个filter helper,symmetric_filter,在C语言API比如zlib、blibz2或openSSL的基础上定义一个filter时非常有用。如果没有filter helper合适,当你准备使用filter来读取数据时应该使用一个InputFilter,当你准备使用filter来写入数据时应该使用一个OutputFilter。在一些情况下,将一个算法用InputFilter表达出来比用OutputFilter表达更容易,反之亦然。在这些情况下,你可以使用更简单的filter,然后使用类模板或函数模板将InputFilter转化为一个OutputFilter,反之亦然。
除了下面最后面的几个过滤实例,我将想你展示怎样用stdio_filter实现一个算法。
2.2. Filter用法实例
Filters通常都在过滤streams和stream buffers中间起连接作用。举个例子,假定你需要压缩一些文本,然后使用Base64加密,最后写入到一个文件中。如果你有适当的OutputFilters,压缩器,base64加密器,你可以参照下例来实现它。
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/filtering_stream.hpp>

namespace io = boost::iostreams;

int main()
{
io::filtering_ostream out;
out.push(compressor());
out.push(base64_encoder());
out.push(file_sink("my_file.txt"));
// write to out using std::ostream interface
}
如所有过滤streams 和 stream buffers一样, filtering_ostream 维护内部的Filters 和Devices链条。 当数据被写入这个chain时,它将按照被输入的顺序通过这些组件。上例所示的尾部的组件是Sink的某个模型,包括std::stream,例如std::cout。
现在,假设你想恢复原来的数据。如果你有合适的InputFilters,解压缩器,base64解密器,你可以参照下例来实现它。
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_stream.hpp>

namespace io = boost::iostreams;

int main()
{
io::filtering_istream in;
in.push(decompressor());
in.push(base64_decoder());
in.push(file_source("my_file.txt"));
// read from in using std::istream interface
}
此处你可以看到另一个filter链条的使用。当数据从这个链条中读取出来时,它会沿反方向从最后添加的组件开始通过这些组件。最后加入的组件可以是Source的一个模型,包括std::istream例如std::cin。
2.3. Shell Comments Filters
假定你想使用一个filter来移除shell-style comments。可以构造如下所示的一个基本算法:你每次检查一个字符,除非碰到一个注释字符,例如'#',否则不处理他们,并传递处理。当你发现一个注释字符的时候,忽略接下来的字符,直到碰到一个换行字符,此换行字符表明算法将重新开始。要注意的是,这个算法包含两个子算法:一个是读取普通字符的算法,一个是读取注释字符。
在接下来的3节中,我将使用stdio_filter、InputFilter和OutputFilter来表达这个算法。
shell_comments_stdio_filter
你可以参考下例使用stdio_filter来描述一个shell comments Filter:
#include <cstdio> // EOF
#include <iostream> // cin, cout
#include <boost/iostreams/filter/stdio.hpp>

class shell_comments_stdio_filter : public stdio_filter {
public:
explicit shell_comments_stdio_filter(char comment_char = '#')
: comment_char_(comment_char)
{ }
private:
void do_filter()
{
bool skip = false;
int c;
while ((c = std::cin.get()) != EOF) {
skip = c == comment_char_ ?
true :
c == ' ' ?
false :
skip;
if (!skip)
std::cout.put(c);
}
}
char comment_char_;
};

} } } // End namespace boost::iostreams:example
虚函数do_filter的实现是简单的:局部变量skip跟踪记录你当前是否正在处理一条注释;while循环从std::cin中读取一个字符并更新skip,如果skip不为true时将c写入到std::cout。
从stdio_filters派生的Filters都是DualUseFilters(双工Filters?),意思是,他们既可以用来输入,也可以用来输出,但不能同时输入输出。因此unix2dos_stdio_filter可以被用来替代下面的shell_comments_input_filter 和 shell_comments_output_filter。
shell_comments_input_filter
下面使用InputFilter来描述shell comments Filter。下例给出了一个典型的单字符InputFIlters
#include <boost/iostreams/categories.hpp> // input_filter_tag
#include <boost/iostreams/char_traits.hpp> // EOF, WOULD_BLOCK
#include <boost/iostreams/operations.hpp> // get, read, putback

namespace io = boost::iostreams;

class my_input_filter {
public:
typedef char char_type;
typedef input_filter_tag category;

template<typename Source>
int get(Source& src)
{
// Attempt to produce one character of filtered
// data, reading from src as necessary. If successful,
// return the character; otherwise return EOF to
// indicate end-of-stream, or WOULD_BLOCK
}

/* Other members */
};
get函数尝试着产生一个过滤后的字符。它通过提供的Source src访问未过滤的字符序列。如果产生了一个字符,get函数就会返回它。否则,get函数会返回状态码EOF或WOULD_BOLOCK。EOF是一个定义在标准头文件中<cstdio>的宏,它用来表明end-of-stream。WOULD_BLOCK是boost::iostreams名字空间中的一个常量,被定义在头文件<boost/iostreams/char_traits.hpp>,用来表明暂时没有输入。
你可以如下例所示改写上面的例子。
#include <boost/iostreams/concepts.hpp> // input_filter

class my_input_filter : public input_filter {
public:
template<typename Source>
int get(Source& src);

/* Other members */
};
此处的input_filter是一个方便的基类,它提供了成员类型char_type和category,以及成员函数close和imbue的实现。我将会简短地介绍close。
你现在可以使用InputFilter 来实现一个shell_comment_filter
#include <boost/iostreams/char_traits.hpp> // EOF, WOULD_BLOCK
#include <boost/iostreams/concepts.hpp> // input_filter
#include <boost/iostreams/operations.hpp> // get

namespace boost { namespace iostreams { namespace example {

class shell_comments_input_filter : public input_filter {
public:
explicit shell_comments_input_filter(char comment_char = '#')
: comment_char_(comment_char), skip_(false)
{ }

template<typename Source>
int get(Source& src)
{
int c;
while (true) {
if ((c = boost::iostreams::get(src)) == EOF || c == WOULD_BLOCK)
break;
skip_ = c == comment_char_ ?
true :
c == ' ' ?
false :
skip_;
if (!skip_)
break;
}
return c;
}

template<typename Source>
void close(Source&) { skip_ = false; }
private:
char comment_char_;
bool skip_;
};

} } } // End namespace boost::iostreams:example
此处的成员变量skip_和 shell_comments_stdio_filter::do_filter中的局部变量skip作用相同。get的实现非常类似于shell_ comments_stdio_filter::do_filter:while循环读取一个字符c,更新skip_并在skip不为真的时候返回c。最大的区别在于你必须处理特殊值WOULD_BLOCK,它表明当前没有输入可获取。
很明显,从头开始实现一个InputFilter比从stdio_filter派生更复杂。使用InputFilter的时候,你需要准备在算法中的任何地方中断;中断时,你必须记录下有关算法当前状态的足够信息,以便以后在继续处理。对于OutputFilters也是一样的。事实上,很多InputFilters和OutputFilters可以被看作有穷状态机。
现在,有关shell_comments_input_filter的只剩下一个问题了:它的实例只能被使用一次。因为当skip_标志被设置时,一个stream可能会被关闭。如果这个stream后来使用另外一个未过滤字符序列重新打开,原来的文本无论是不是注释都会被过滤掉。
可以通过将filter设置为可关闭的来解决这个问题。这样的话,你就必须实现一个close函数。同时你也必须将category标志改为closeable_tag,以便告诉Iostream库你的filter实现了close。
下例示范了一个改进过后的Filter:
namespace boost { namespace iostreams { namespace example {

class shell_comments_input_filter : public input_filter {
public:
shell_comments_input_filter();

template<typename Source>
int get(Source& src);

template<typename Source>
void close(Source&) { skip_ = false; }
private:
bool skip_;
};

} } } // End namespace boost::iostreams:example
shell_comments_output_filter
接下来,让我们使用OutputFilter来描述一个shell comments Filter。下例示范了一个典型的单字符OutputFilter
#include <boost/iostreams/categories.hpp>
#include <boost/iostreams/operations.hpp> // put, write

namespace io = boost::iostreams;

class my_output_filter {
public:
typedef char char_type;
typedef output_filter_tag category;

template<typename Sink>
bool put(Sink& dest, int c)
{
// Attempt to consume the given character of unfilitered
// data, writing filtered data to dest as appropriate.
// Return true if the character was successfully consumed.
}

/* Other members */
};
put函数尝试这过滤单个字符c,并输出到Sink dest中。put函数通过基础的i/o操作put和write访问dest。这连个函数都有可能失败:iostreams::put可能返回false,iostreams::write使用的字符可能比要求使用的字符少。
你可以如下例所示对上面的例子进行改写。
#include <boost/iostreams/concepts.hpp> // output_filter

class my_output_filter : public output_filter {
public:
template<typename Sink>
bool put(Sink& dest, int c);

/* Other members */
};
此处的out_filter是一个方便的基类,它提供了成员类型char_type和category,以及close和imbue成员函数的实现。
现在你可以使用一个OutputFilter来实现一个shell comment Filter:
#include <boost/iostreams/concepts.hpp> // output_filter
#include <boost/iostreams/operations.hpp> // put

namespace boost { namespace iostreams { namespace example {

class shell_comments_output_filter : public output_filter {
public:
explicit shell_comments_output_filter(char comment_char = '#')
: comment_char_(comment_char), skip_(false)
{ }

template<typename Sink>
bool put(Sink& dest, int c)
{
skip_ = c == comment_char_ ?
true :
c == ' ' ?
false :
skip_;

if (skip_)
return true;

return iostreams::put(dest, c);
}

template<typename Source>
void close(Source&) { skip_ = false; }
private:
char comment_char_;
bool skip_;
};

} } } // End namespace boost::iostreams:example
成员函数put首先检查提供的字符c并更新成员变量skip_;然后,除非skip_为true,否则尝试着写入c。成员函数close简单的清除skip_标志从而使Filter能被重新打开。
Filters 可以用来修改字符序列。例如,你可以通过一个filter将某一个字符全部替换成另外一个字符,将所有字母字符转换为小写格式,或者加密一个文档。有时filter只是充当observer,例如你可以使用一个filter来计算指定单词出现的次数。
InputFilters和OutputFilters
Iostreams库支持两种简单类型的 Filters InputFilters和OutputFilters。InputFilters代表一种"pull"过滤模型:未过滤的数据以source的形式提供出来,Filter将会产生一定量字符的过滤序列。过滤后的序列是以递增的形式产生的,也就是为了过滤一个指定的字符序列,Filter必须被调用多次。OutputFilters代表一种"push"过滤模型:一个未过滤的字符序列和一个Sink需要被提供,另外Filter将会过滤这些字符,并将他们写入Sink。与InputFilters类似,OutputFilters以递增的方式处理数据。
最简单的InputFilters和OutputFilters一次处理一个字符。这种类型的Filter很容易写,但是比那些一次处理多个字符的Filters效率不佳。一次处理多个字符的Filters被称为Multi-Character filters。
Filter Helpers
Iostreams库提供了许多工具以便更容易使用Filter
* aggregate_filter 允许程序员定义一种Filter,它从一个std::vector中读取未过滤数据,并将过滤后的数据写入到另一个std::vector中。
* stdio_filter 允许程序员定义一种Filter,它从标准输入中读取未过滤数据,并将过滤后的数据写入到标准输出中。
* symmetric_filter 允许程序员定义一种Filter,它从数组中读取未过滤数据,并将过滤后的数据写入到另一个数组中。
* finite_state_filter 允许程序员将Filter定义为一个有穷状态机。它目前还不是这个库的正式组成部分。
选择一种 Filter 概念
假定你想利用一个 Filter来执行给定的过滤任务。你将如何决定是使用InputFilter还是OutputFilter,或者使用Filter Helpers?上面提到的两个Filter helpers,aggregate_filter 和 stdio_filter, 具有很高的内存使用,并且只能处理以一个清楚界定结尾的字符串。他们允许过滤算法以一种很简单的方式表达出来,但同时也提供了对过滤的一种很好的介绍。第三个filter helper,symmetric_filter,在C语言API比如zlib、blibz2或openSSL的基础上定义一个filter时非常有用。如果没有filter helper合适,当你准备使用filter来读取数据时应该使用一个InputFilter,当你准备使用filter来写入数据时应该使用一个OutputFilter。在一些情况下,将一个算法用InputFilter表达出来比用OutputFilter表达更容易,反之亦然。在这些情况下,你可以使用更简单的filter,然后使用类模板或函数模板将InputFilter转化为一个OutputFilter,反之亦然。
除了下面最后面的几个过滤实例,我将想你展示怎样用stdio_filter实现一个算法。
2.2. Filter用法实例
Filters通常都在过滤streams和stream buffers中间起连接作用。举个例子,假定你需要压缩一些文本,然后使用Base64加密,最后写入到一个文件中。如果你有适当的OutputFilters,压缩器,base64加密器,你可以参照下例来实现它。













如所有过滤streams 和 stream buffers一样, filtering_ostream 维护内部的Filters 和Devices链条。 当数据被写入这个chain时,它将按照被输入的顺序通过这些组件。上例所示的尾部的组件是Sink的某个模型,包括std::stream,例如std::cout。
现在,假设你想恢复原来的数据。如果你有合适的InputFilters,解压缩器,base64解密器,你可以参照下例来实现它。













此处你可以看到另一个filter链条的使用。当数据从这个链条中读取出来时,它会沿反方向从最后添加的组件开始通过这些组件。最后加入的组件可以是Source的一个模型,包括std::istream例如std::cin。
2.3. Shell Comments Filters
假定你想使用一个filter来移除shell-style comments。可以构造如下所示的一个基本算法:你每次检查一个字符,除非碰到一个注释字符,例如'#',否则不处理他们,并传递处理。当你发现一个注释字符的时候,忽略接下来的字符,直到碰到一个换行字符,此换行字符表明算法将重新开始。要注意的是,这个算法包含两个子算法:一个是读取普通字符的算法,一个是读取注释字符。
在接下来的3节中,我将使用stdio_filter、InputFilter和OutputFilter来表达这个算法。
shell_comments_stdio_filter
你可以参考下例使用stdio_filter来描述一个shell comments Filter:





























虚函数do_filter的实现是简单的:局部变量skip跟踪记录你当前是否正在处理一条注释;while循环从std::cin中读取一个字符并更新skip,如果skip不为true时将c写入到std::cout。
从stdio_filters派生的Filters都是DualUseFilters(双工Filters?),意思是,他们既可以用来输入,也可以用来输出,但不能同时输入输出。因此unix2dos_stdio_filter可以被用来替代下面的shell_comments_input_filter 和 shell_comments_output_filter。
shell_comments_input_filter
下面使用InputFilter来描述shell comments Filter。下例给出了一个典型的单字符InputFIlters






















get函数尝试着产生一个过滤后的字符。它通过提供的Source src访问未过滤的字符序列。如果产生了一个字符,get函数就会返回它。否则,get函数会返回状态码EOF或WOULD_BOLOCK。EOF是一个定义在标准头文件中<cstdio>的宏,它用来表明end-of-stream。WOULD_BLOCK是boost::iostreams名字空间中的一个常量,被定义在头文件<boost/iostreams/char_traits.hpp>,用来表明暂时没有输入。
你可以如下例所示改写上面的例子。









此处的input_filter是一个方便的基类,它提供了成员类型char_type和category,以及成员函数close和imbue的实现。我将会简短地介绍close。
你现在可以使用InputFilter 来实现一个shell_comment_filter






































此处的成员变量skip_和 shell_comments_stdio_filter::do_filter中的局部变量skip作用相同。get的实现非常类似于shell_ comments_stdio_filter::do_filter:while循环读取一个字符c,更新skip_并在skip不为真的时候返回c。最大的区别在于你必须处理特殊值WOULD_BLOCK,它表明当前没有输入可获取。
很明显,从头开始实现一个InputFilter比从stdio_filter派生更复杂。使用InputFilter的时候,你需要准备在算法中的任何地方中断;中断时,你必须记录下有关算法当前状态的足够信息,以便以后在继续处理。对于OutputFilters也是一样的。事实上,很多InputFilters和OutputFilters可以被看作有穷状态机。
现在,有关shell_comments_input_filter的只剩下一个问题了:它的实例只能被使用一次。因为当skip_标志被设置时,一个stream可能会被关闭。如果这个stream后来使用另外一个未过滤字符序列重新打开,原来的文本无论是不是注释都会被过滤掉。
可以通过将filter设置为可关闭的来解决这个问题。这样的话,你就必须实现一个close函数。同时你也必须将category标志改为closeable_tag,以便告诉Iostream库你的filter实现了close。
下例示范了一个改进过后的Filter:

















shell_comments_output_filter
接下来,让我们使用OutputFilter来描述一个shell comments Filter。下例示范了一个典型的单字符OutputFilter




















put函数尝试这过滤单个字符c,并输出到Sink dest中。put函数通过基础的i/o操作put和write访问dest。这连个函数都有可能失败:iostreams::put可能返回false,iostreams::write使用的字符可能比要求使用的字符少。
你可以如下例所示对上面的例子进行改写。










此处的out_filter是一个方便的基类,它提供了成员类型char_type和category,以及close和imbue成员函数的实现。
现在你可以使用一个OutputFilter来实现一个shell comment Filter:



































成员函数put首先检查提供的字符c并更新成员变量skip_;然后,除非skip_为true,否则尝试着写入c。成员函数close简单的清除skip_标志从而使Filter能被重新打开。