Предположим, вы хотите написать фильтр, который заменяет каждый символ вкладки одним или несколькими символами пространства таким образом, чтобы документ отображался без изменений. Основной алгоритм выглядит следующим образом: Вы просматриваете символы по одному за раз, пересылая ихкак естьи отслеживая текущий номер столбца. Когда вы встречаете символ вкладки, вы заменяете его последовательностью пространственных символов, длина которых зависит от текущего количества столбцов. Когда вы сталкиваетесь с персонажем новой линии, вы перенаправляете его и сбрасываете счет столбца.
void put_char(int c)
{
std::cout.put(c);
if (c == '\n') {
col_no_ = 0;
} else {
++col_no_;
}
}
<put_char>Вы можете использовать<do_filter>следующим образом:
void do_filter()
{
int c;
while ((c = std::cin.get()) != EOF) {
if (c == '\t') {
int spaces = tab_size_ - (col_no_ % tab_size_);
for (; spaces > 0; --spaces)
put_char(' ');
} else {
put_char(c);
}
}
}
<while>петля читает символ из<std::cin>и записывает его в<std::cout>, если только он не является символом вкладки, и в этом случае он пишет соответствующее количество символов пространства<std::cout>.
Вы можете выразить расширяющий вкладку фильтр какInputFilterследующим образом:
#include<boost/iostreams/char_traits.hpp>// EOF, WOULD_BLOCK#include<boost/iostreams/concepts.hpp>// input_filter#include<boost/iostreams/operations.hpp>// getnamespace boost { namespace iostreams { namespace example {
class tab_expanding_input_filter : public input_filter {
public:
explicit tab_expanding_input_filter(int tab_size = 8)
: tab_size_(tab_size), col_no_(0), spaces_(0)
{
assert(tab_size > 0);
}
template<typename Source>
int get(Source& src);
template<typename Source>
void close(Source&);
private:
int get_char(int c);
int tab_size_;
int col_no_;
int spaces_;
};
} } } // End namespace boost::iostreams:example
Давайте сначала рассмотрим функцию помощника<get_char>:
int get_char(int c)
{
if (c == '\n') {
col_no_ = 0;
} else {
++col_no_;
}
return c;
}
Эта функция обновляет подсчет столбцов на основе заданного символа<c>и возвращает<c>. Используя<get_char>вы можете реализовать<get>следующим образом:
template<typename Source>
int get(Source& src)
{
if (spaces_ > 0) {
--spaces_;
return get_char(' ');
}
int c;
if ((c = iostreams::get(src)) == EOF || c == WOULD_BLOCK)
return c;
if (c != '\t')
return get_char(c);
// Found a tab. Call this filter recursively.
spaces_ = tab_size_ - (col_no_ % tab_size_);
return this->get(src);
}
Похожая ситуация наблюдается и в<line_wrapping_input_filter::get>. Поскольку<get>может вернуть только один символ за раз, всякий раз, когда символ вкладки должен быть заменен последовательностью пространственного символа, может быть возвращен только первый пространственный символ. Остальное должно быть возвращено последующими призывами<get>. Для хранения числа таких пространственных символов используется переменная<spaces_>.
Реализация начинается с проверки того, остаются ли какие-либо космические символы. Если это так, то он уменьшается<spaces_>и возвращает пространство. В противном случае персонаж читается<src>. Обычные символы, а также специальные значения<EOF>и<WOULD_BLOCK>возвращаютсякак есть. Когда встречается символ вкладки, записывается количество пространств, которое должно быть возвращено будущими вызовами получения, и возвращается пространственный символ.
Как обычно, функция<close>сбрасывает состояние фильтра:
void close(Source&)
{
col_no_ = 0;
spaces_ = 0;
}
tab_expanding_output_filter
Вы можете выразить расширяющий вкладку фильтр какOutputFilterследующим образом:
#include<boost/iostreams/concepts.hpp>// output_filter#include<boost/iostreams/operations.hpp>// putnamespace boost { namespace iostreams { namespace example {
class tab_expanding_output_filter : public output_filter {
public:
explicit tab_expanding_output_filter(int tab_size = 8)
: tab_size_(tab_size), col_no_(0), spaces_(0)
{
assert(tab_size > 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 tab_size_;
int col_no_;
int spaces_;
};
} } } // End namespace boost::iostreams:example
Функция имплементации помощника<put_char>такая же, как у<line_wrapping_output_filter::put_char>: она записывает данный символ в<std::cout>и увеличивает номер столбца, если только символ не является новой линией, и в этом случае номер столбца сбрасывается.
template<typename Sink>
bool put_char(Sink& dest, int c)
{
if (!iostreams::put(dest, c))
returnfalse;
if (c != '\n')
++col_no_;
else
col_no_ = 0;
returntrue;
}
При помощи<put_char>можно реализовать<put>следующим образом:
template<typename Sink>
bool put(Sink& dest, int c)
{
for (; spaces_ > 0; --spaces_)
if (!put_char(dest, ' '))
returnfalse;
if (c == '\t') {
spaces_ = tab_size_ - (col_no_ % tab_size_) - 1;
return this->put(dest, ' ');
}
return put_char(dest, c);
}
Реализация начинается с попытки написать любые космические символы, оставшиеся от ранее встречавшихся вкладок. В случае успеха исследуется данный персонаж<c>. Если<c>не является символом вкладки, он пытается написать его<dest>. В противном случае он вычисляет количество пробелов, которые должны быть вставлены, и называет себя рекурсивно. Использование рекурсии здесь избавляет нас от необходимости уменьшать переменную члена<spaces_>в двух разных точках кода.
Обратите внимание, что после того, как столкнется персонаж вкладки, он вернется ложным, пока не будут написаны все связанные космические символы.
Как обычно, функция<close>сбрасывает состояние фильтра:
Статья Tutorial раздела может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.