int main()
{
string from, to;
cin >> from >> to; // вводим имена исходного
// и целевого файлов
ifstream is(from.c_str()); // открываем поток ввода
ofstream os(to.c_str()); // открываем поток вывода
istream_iterator<string> ii(is); // создаем итератор ввода
// из потока
istream_iterator<string> eos; // сигнальная метка ввода
ostream_iterator<string> oo(os,"\n"); // создаем итератор
// вывода в поток
vector<string> b(ii,eos); // b — вектор, который
// инициализируется
// данными из потока ввода
sort(b.begin(),b.end()); // сортировка буфера
copy(b.begin(),b.end(),oo); // буфер копирования для вывода
}
Итератор
eos
— это сигнальная метка, означающая “конец ввода.” Когда поток
istream
достигает конца ввода (который часто называется
eof
), его итератор
istream_iterator
становится равным итератору
istream_iterator
, который задается по умолчанию и называется
eos
.
Обратите внимание на то, что мы инициализируем объект класса vector парой итераторов. Пара итераторов
(a,b)
, инициализирующая контейнер, означает следующее: “Считать последовательность
[a:b]
в контейнер”. Естественно, для этого мы использовали пару итераторов
(ii,eos)
— начало и конец ввода. Это позволяет нам не использовать явно оператор
>>
и функцию
push_back()
. Мы настоятельно не рекомендуем использовать альтернативный вариант.
vector<string> b(max_size); // не пытайтесь угадать объем входных
// данных
copy(ii,eos,b.begin());
Люди, пытающиеся угадать максимальный размер ввода, обычно недооценивают его, переполняют буфер и создают серьезные проблемы как для себя, так и для пользователей. Переполнение буфера может также создать опасность для сохранности данных.
ПОПРОБУЙТЕ
Приведите программу в рабочее состояние и протестируйте ее на небольшом файле, скажем, содержащем несколько сотен слов. Затем испытайте “настоятельно не рекомендованную версию”, в которой объем входных данных угадывается, и посмотрите, что произойдет при переполнении буфера ввода
b
. Обратите внимание на то, что наихудшим сценарием является тот, в котором вы не замечаете ничего плохого и передаете программу пользователям.
В нашей маленькой программе мы считываем слова, а затем упорядочиваем их. Пока все, что мы делаем, кажется очевидным, но почему мы записываем слова в “неправильные” ячейки, так что потом вынуждены их сортировать? Кроме того, что еще хуже, оказывается, что мы записываем слова и выводим их на печать столько раз, сколько они появляются в потоке ввода.
Последнюю проблему можно решить, используя алгоритм
unique_copy()
вместо алгоритма
copy()
. Функция
unique_copy()
просто не копирует повторяющиеся идентичные значения. Например, при вызове обычной функции
copy()
программы введет строку
the man bit the dog
и выведет на экран слова
bit
dog
man
the
the
Если же используем алгоритм
unique_copy()
, то программа выведет следующие слова:
bit
dog
man
the
Откуда взялись переходы на новую строку? Вывод с разделителями настолько распространен, что конструктор класса
ostream_iterator
позволяет вам (при необходимости) указывать строку, которая может быть выведена после каждого значения.
ostream_iterator<string> oo(os,"\n"); // создает итератор для
// потока вывода
Очевидно, что переход на новую строку — это распространенный выбор для вывода, позволяющий людям легче разбираться в результатах, но, возможно, вы предпочли бы использовать пробелы? Мы могли бы написать следующий код:
ostream_iterator<string> oo(os," "); // создает итератор для потока
// вывода
В этом случае результаты вывода выглядели бы так:
bit dog man the
21.7.3. Использование класса set для поддержания порядка
Существует еще более простой способ получить такой вывод: использовать контейнер
set
, а не
vector
.
int main()
{
string from, to;
cin >> from >> to; // имена исходного и целевого файлов
ifstream is(from.c_str()); // создаем поток ввода
ofstream os(to.c_str()); // создаем поток вывода
istream_iterator<string> ii(is); // создаем итератор ввода