buffer.clear();
// заполняем объект buffer
}
Если объект buffer
находится в состоянии bad()
или существуют проблемы с источником данных, работа прекращается; в противном случае объект buffer
очищается и выполняется новая попытка. Мы должны очистить объект buffer
, потому что попадем в “цикл заполнения”, только если попытка ввода закончится неудачей. Обычно это происходит, если вызывается функция eof()
для объекта buffer;
иначе говоря, когда в объекте buffer
не остается больше символов для чтения. Обработка состояний потока всегда запутанна и часто является причиной очень тонких ошибок, требующих утомительной отладки. К счастью, остаток цикла заполнения вполне очевиден.
string line;
getline(source,line); // вводим строку line из потока source
// при необходимости выполняем замену символов
for (int i =0; 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
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 word;
while (ps>>word) vs.push_back(word); // ввод слов
sort(vs.begin(),vs.end()); // сортировка в лексикографическом
// порядке
for (int i=0; i
if (i==0 || vs[i]!=vs[i–1]) cout << vs[i] << endl;
}