Шаг 520.
Библиотека STL. Ввод-вывод с использованием потоковых классов. Классы потоковых буферов. Пользовательские буферы ввода

    На этом шаге мы рассмотрим особенности создания пользовательских буферов ввода.

    В сущности, механизм ввода работает по тем же принципам, что и механизм вывода. Однако для ввода также существует возможность отмены последнего чтения. Функции sungetc() (вызывается функцией unget() входного потока данных) и sputbackc() (вызывается функцией putback() входного потока данных) используются для восстановления потокового буфера в состоянии перед последним чтением. Также существует возможность чтения следующего символа без перемещения позиции чтения. Следовательно, при реализации чтения из потокового буфера приходится переопределять больше функций, чем при реализации записи в потоковый буфер.

    Для буфера, используемого для записи символов, поддерживаются три указателя, которые могут быть получены функциями eback(), gptr() и egptr() (рисунок 1):


Рис.1. Интерфейс чтения из потоковых буферов

    Символы, находящиеся между начальной и конечной позициями, были переданы из внешнего представления в память программы, но еще ожидают обработки.

    Одиночные символы читаются функциями sgetc() и sbumpc(). Отличие между этими функциями состоит в том, что функция sbumpc() увеличивает указатель текущей позиции ввода, а функция sgetc() этого не делает. Если буфер будет полностью прочитан (gptr()==egptr()), значит, доступных символов нет и буфер необходимо заполнять заново. Для этого вызывается виртуальная функция underflow(), отвечающая за чтение данных. С другой стороны, функция sbumpc() при отсутствии символов вызывает виртуальную функцию uflow(). По умолчанию uflow() просто вызывает underflow(), а затем увеличивает указатель. По умолчанию версия underflow() в базовом классе basic_streambuf возвращает EOF, то есть признак невозможности дальнейшего чтения с использованием стандартной реализации.

    Функция sgetn() предназначена для чтения сразу нескольких символов. Она перепоручает работу виртуальной функции xsgetn(). В реализации по умолчанию xsgetn() просто читает символы, вызывая для каждого из них sbumpc(). По аналогии с функцией xsputn() при записи функция xsgetn() используется для оптимизации чтения нескольких символов.

    В отличие от вывода для ввода недостаточно переопределить одну функцию. Вам придется либо выполнить настройку буфера, либо, по крайней мере, реализовать функции underflow() и uflow(). Дело в том, что функция underflow() не перемещается за текущий символ, однако она может быть вызвана из sgetc(). Перемещение к следующему символу приходится выполнять путем манипуляций с буфером или вызовом uflow(). В любом случае функция underflow() должна быть реализована для любого потокового буфера, поддерживающего чтение символов. Если реализованы обе функции, underflow() и uflow(), в настройке буфера нет необходимости.

    Настройка буфера чтения осуществляется функцией setg(), получающей следующие три аргумента (именно в таком порядке):

    В отличие от setp() функция setg() вызывается с тремя аргументами. Это необходимо для того, чтобы вы могли зарезервировать память для символов, возвращаемых в поток данных. Таким образом, при настройке буфера ввода желательно, чтобы некоторые символы (по крайней мере один) уже были прочитаны, но еще не сохранены в буфере.

    Как упоминалось в предыдущих шагах, символы можно вернуть в буфер чтения с помощью функций sputbackc() и sungetc(). Функция sputbackc() получает возвращаемый символ в аргументе и проверяет, что именно этот символ был прочитан последним. Обе функции уменьшают указатель текущей позиции чтения, если это возможно. Очевидно, это возможно только в том случае, если указатель чтения не находится в начале буфера. При попытке возврата символа в начале буфера вызывается виртуальная функция pbackfail(). Переопределяя эту функцию, можно реализовать механизм восстановления прежней позиции чтения даже в этом случае. В базовом классе basic_streambuf соответствующее поведение не определено. Таким образом, на практике возврат на произвольное количество символов невозможен. Для потоков данных, не использующих буферизацию, следует реализовать функцию pbackfail(), потому что в общем случае предполагается, что хотя бы один символ может быть возвращен в поток.

    Когда буфер заполняется заново, возникает другая проблема: если прежние данные не были сохранены в буфере, возврат даже одного символа невозможен. По этой причине реализация underflow() часто перемещает несколько последних символов (например, четыре) в начало буфера и присоединяет читаемые символы после ннх. Это позволяет вернуть хотя бы несколько символов перед тем, как будет вызвана функция pbackfail().

    На следующем шаге мы рассмотрим пример использования потоковых буферов ввода.




Предыдущий шаг Содержание Следующий шаг