假设你想编写一个过滤器,将文本行换行,以确保没有一行超过某个最大长度。为简单起见,我们不要费心在词语边界换行或插入连字符。基本算法如下:你逐个检查字符,按原样转发,并跟踪当前列号。当你遇到换行符时,你转发它并重置列计数。当列计数达到最大值时,你在当前字符之前插入一个换行符,并重置列计数。
在接下来的三个部分中,我将把此算法表达为一个 stdio_filter
、一个 InputFilter 和一个 OutputFilter。源代码可以在 <libs/iostreams/example/line_wrapping_filter.hpp>
头文件中找到。这些示例受到 James Kanze 的 LineWrappingInserter.hh
的启发(参见 [Kanze])。
line_wrapping_stdio_filter
你可以将换行过滤器表达为一个 stdio_filter
,如下所示
#include <cstdio> // EOF #include <iostream> // cin, cout #include <boost/iostreams/filter/stdio.hpp> namespace boost { namespace iostreams { namespace example { class line_wrapping_stdio_filter : public stdio_filter { public: explicit line_wrapping_stdio_filter(int line_length = 80) : line_length_(line_length), col_no_(0) { } private: void do_filter(); void do_close(); void put_char(int c); int line_length_; int col_no_; }; } } } // End namespace boost::iostreams:example
让我们先看看辅助函数 put_char
的定义
void put_char(int c) { std::cout.put(c); if (c != '\n') ++col_no_; else col_no_ = 0; }
此函数将给定字符写入 std::cout
并递增列号,除非字符是换行符,在这种情况下,列号将重置。使用 put_char
,你可以实现 virtual
函数 do_filter
,如下所示
void do_filter() { int c; while ((c = std::cin.get()) != EOF) { if (c != '\n' && col_no_ >= line_length_) put_char('\n'); put_char(c); } }
while
循环只是从 std::cin
读取一个字符并将其写入 std::cout
,并根据需要插入额外的换行符以防止列计数超过 line_length_
。
最后,成员函数 do_close
覆盖了 stdio_filter
中声明的 private
virtual
函数。
void do_close() { col_no_ = 0; }
它的目的是在流关闭时重置过滤器的状态。
line_wrapping_input_filter
你可以将换行过滤器表达为一个 InputFilter,如下所示
#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 line_wrapping_input_filter : public input_filter { public: explicit line_wrapping_input_filter(int line_length = 80) : line_length_(line_length), col_no_(0), has_next_(false) { } template<typename Source> int get(Source& src); template<typename Sink> void close(Sink&); private: int get_char(int c); int line_length_; int col_no_; int next_; int has_next_; }; } } } // End namespace boost::iostreams:example
让我们先看看辅助函数 get_char
int get_char(int c) { if (c != '\n') ++col_no_; else col_no_ = 0; return c; }
此函数根据给定字符 c
更新列计数,然后返回 c
。使用 get_char
,你可以实现 get
,如下所示
template<typename Source> int get(Source& src) { if (has_next_) { has_next_ = false; return get_char(next_); } int c; if ((c = iostreams::get(src)) == EOF || c == WOULD_BLOCK) return c; if (c != '\n' && col_no_ >= line_length_) { next_ = c; has_next_ = true; return get_char('\n'); } return get_char(c); }
一个不是 MultiCharacterFilter 的 InputFilter 只能一次返回一个字符。因此,如果你希望在从 src
读取的字符 c
之前插入一个换行符,你必须存储 c
并在下一次调用 get
时返回它。成员变量 next_
用于存储这样的字符;成员变量 has_next_
用于跟踪是否存储了这样的字符。
get
的实现首先检查是否有存储的字符,如果有就返回它。否则,它尝试从 src
读取一个字符。如果无法读取任何字符,它将返回特殊值之一 EOF
或 WOULD_BLOCK
。否则,它将检查是否必须插入换行符。如果是,它将存储当前字符并返回换行符。否则,它将返回当前字符。
最后,成员函数 close
重置过滤器的状态
template<typename Sink> void close(Sink&) { col_no_ = 0; has_next_ = false; }
line_wrapping_output_filter
你可以将换行过滤器表达为一个 OutputFilter,如下所示
#include <boost/iostreams/concepts.hpp> // output_filter #include <boost/iostreams/operations.hpp> // put namespace boost { namespace iostreams { namespace example { class line_wrapping_output_filter : public output_filter { public: explicit line_wrapping_output_filter(int line_length = 80) : line_length_(line_length), col_no_(0) { } template<typename Sink> bool put(Sink& dest, int c); template<typename Sink> void close(Sink&); private: template<typename Sink> bool put_char(Sink& dest, int c); int line_length_; int col_no_; }; } } } // End namespace boost::iostreams:example
让我们先看看辅助函数 put_char
template<typename Sink> bool put_char(Sink& dest, int c) { if (!iostreams::put(dest, c)) return false; if (c != '\n') ++col_no_; else col_no_ = 0; return true; }
此函数尝试将字符 c
写入给定的 Sink 并更新列计数(如果成功)。使用 put_char
,你可以实现 put
,如下所示
template<typename Sink> bool put(Sink& dest, int c) { if (c != '\n' && col_no_ >= line_length_ && !put_char(dest, '\n')) return false; return put_char(dest, c); }
此函数首先检查给定字符和列计数以查看是否必须插入换行符。如果是,它将尝试使用 put_char
写入换行符,如果操作失败则返回 false。否则,它将尝试使用 put_char
写入给定字符。注意,如果成功插入换行符但尝试写入给定字符失败,则列计数将更新以反映换行符,这样下次尝试写入给定字符时不会导致换行符被插入。
最后,成员函数 close
重置过滤器的状态
template<typename Sink> void close(Sink&) { col_no_ = 0; }
© 版权所有 2008 CodeRage, LLC
© 版权所有 2004-2007 Jonathan Turkanis
使用、修改和分发受 Boost 软件许可证版本 2.0 的约束。(参见附带文件 LICENSE_1_0.txt 或 https://boost.ac.cn/LICENSE_1_0.txt 上的副本)