Шрифт:
while (!(buffer>>s)) { // попытка прочитать символы из буфера
if (buffer.bad || !source.good) return *this;
buffer.clear;
// заполняем объект buffer
}
Если объект
buffer
находится в состоянии bad
или существуют проблемы с источником данных, работа прекращается; в противном случае объект buffer
очищается и выполняется новая попытка. Мы должны очистить объект buffer
, потому что попадем в “цикл заполнения”, только если попытка ввода закончится неудачей. Обычно это происходит, если вызывается функция eof
для объекта buffer;
иначе говоря, когда в объекте buffer
не остается больше символов для чтения. Обработка состояний потока всегда запутанна и часто является причиной очень тонких ошибок, требующих утомительной отладки. К счастью, остаток цикла заполнения вполне очевиден.
string line;
getline(source,line); // вводим строку line из потока source
// при необходимости выполняем замену символов
for (int i =0; i<line.size; ++i)
if (is_whitespace(line[i]))
line[i]= ' '; // в пробел
else if (!sensitive)
line[i] = tolower(line[i]); // в нижний регистр
buffer.str(line); // вводим строку в поток
Считываем строку в объект
buffer
, затем просматриваем каждый символ строки в поисках кандидатов на замену. Функция is_whitespace
является членом класса Punct_stream
, который мы определим позднее. Функция tolower
— это стандартная библиотечная функция, выполняющая очевидное задание, например превращает символ A
в символ a
(см. раздел 11.6). После правильной обработки строки
line
ее необходимо записать в поток istringstream
. Эту задачу выполняет функция buffer.str(line);
эту команду можно прочитать так: “Поместить строку из объекта buffer
класса istringstream
в объект line
”. Обратите внимание на то, что мы “забыли” проверить состояние объекта
source
после чтения данных с помощью функции getline
. Это не обязательно, поскольку в начале цикла выполняется проверка условия !source.good
. Как всегда, оператор
>>
возвращает ссылку на поток *this
(раздел 17.10). Проверка разделителей проста; мы сравниваем символ с каждым символом из строки, в которой записаны разделители.
bool Punct_stream::is_whitespace(char c)
{
for (int i = 0; i<white.size; ++i)
if (c==white[i]) return true;
return false;
}
Напомним, что поток
istringstream
обрабатывает обычные разделители (например, символы перехода на новую строку или пробел) по-прежнему, поэтому никаких особых действий предпринимать не надо. Осталась одна загадочная функция.
Punct_stream::operator bool
{
return !(source.fail || source.bad) && source.good;
}
Обычное использование потока
istream
сводится к проверке результата оператора >>
. Рассмотрим пример.
while (ps>>s) { /* ... */ }
Это значит, что нам нужен способ для проверки результата выполнения инструкции
ps>>s
, представленного в виде булевого значения. Результатом инструкции ps>>s
является объект класса Punct_stream
, поэтому нам нужен способ неявного преобразования класса Punct_stream
в тип bool
. Эту задачу решает функция operator bool
в классе Punct_stream
. Функция-член operator
bool
определяет преобразование класса Punct_stream
в тип bool
. В частности, она возвращает значение true
, если эта операция над классом Punct_stream
прошла успешно. Теперь можем написать программу.
int main
// вводит текст и создает упорядоченный список всех слов
// из заданного текста, игнорируя знаки пунктуации и регистры,
// а также удаляя дубликаты из полученного результата
{
Punct_stream ps(cin);
ps.whitespace(";:,.?!\"{}<>/&$@#%^*|~"); // \" в строке
// означает "
ps.case_sensitive(false);
cout << "Пожалуйста, введите слова \n";
vector<string> vs;
string word;
while (ps>>word) vs.push_back(word); // ввод слов