Шрифт:
Интервал:
Закладка:
Функция compress выглядит более сложно. Мы также написали для нее небольшую вспомогательную функцию. Поэтому сначала взглянем на функцию occurrences. На рис. 6.12 показано, как она работает.

Функция occurences принимает два параметра: итератор, указывающий на начало последовательности символов внутри диапазона данных, и конечный итератор этого набора. С помощью find_if мы находим первый символ, отличный от символа, на который изначально указывал итератор. На рис. 6.12 данный итератор называется diff. Разность между этой новой позицией и старой позицией итератора равна количеству одинаковых элементов (на рис. 6.12 diff-it равно 6). После выполнения подсчетов итератор diff можно использовать повторно для выполнения нового поиска. Так что мы помещаем diff, символ поддиапазона и длину поддиапазона в кортеж и возвращаем его.
Имея подобную информацию, можно переходить от поддиапазона к поддиапазону и помещать промежуточные результаты в сжатую целевую строку:
for (auto it (begin(s)); it != end_it;) {
const auto [next_diff, c, n] (occurrences(it, end_it));
r << c << n;
it = next_diff;
}
Дополнительная информация
В шаге 4 мы упоминали, что функция decompress небезопасна. Более того, ее легко использовать со злым умыслом.
Представьте следующую входную строку: "a00000". Ее сжатие приведет к результату "a1", поскольку в ней содержится только один символ, 'a'. За ним следует пять символов '0', что даст результат "05". Итоговый результат выглядит как "a105". К сожалению, эта сжатая строка гласит: «Символ 'a', повторенный 105 раз». Это не имеет ничего общего с нашей исходной строкой. При ее декомпрессии мы получим строку длиной 105 символов вместо строки, состоящей всего из шести символов. Представьте то же самое для более крупных чисел — пользова тель может легко нарушить использование кучи, поскольку наш алгоритм не готов к таким входным данным.
Чтобы это предотвратить, функция compress может, например, отвергать входные данные, содержащие числа, или особым образом их помечать. А алгоритм decompress способен принимать еще одно условное выражение, которое ограничивает максимальный размер итоговой строки. Попробуйте выполнить данное упражнение самостоятельно.
Глава 7
Строки, классы потоков и регулярные выражения
В этой главе:
□ создание, конкатенация и преобразование строк;
□ удаление пробелов из начала и конца строк;
□ преимущества использования std::string без затрат на создание объектов std::string;
□ чтение значений из пользовательского ввода;
□ подсчет всех слов в файле;
□ форматирование ваших выходных данных с помощью манипуляторов потока ввода/вывода;
□ инициализация сложных объектов из файла вывода;
□ заполнение контейнеров с применением итераторов std::istream;
□ вывод любых данных на экран с помощью итераторов std::ostream;
□ перенаправление выходных данных в файл для конкретных разделов кода;
□ создание пользовательских строковых классов путем наследования std::char_traits;
□ токенизация входных данных с помощью библиотеки для работы с регулярными выражениями;
□ удобный и красивый динамический вывод чисел на экран в зависимости от контекста;
□ перехват читабельных исключений для ошибок потока std::iostream.
Введение
Данная глава посвящена обработке строк, анализу и выводу на экран произвольных данных. Для этих задач STL предоставляет потоковую библиотеку для работы с вводом-выводом. Библиотека состоит из следующих классов, показанных в серых прямоугольниках (рис. 7.1).

Стрелки показывают схему наследования классов. Рисунок на первый взгляд может показаться непонятным, но в рамках главы мы рассмотрим все классы и познакомимся с ними по очереди. Если мы попробуем обратиться к документации С++ в STL, то не найдем упомянутые классы по конкретно этим именам. Причина такова: названия, приведенные на рис. 7.1, мы видим только как программисты приложений. Но они, по сути, являются просто ключевыми словами для классов с префиксом имени класса basic_ (например, в документации STL проще найти класс basic_istream, в отличие от istream). Классы для работы с потоками, которые начинаются с префикса basic_*, являются шаблонными, они могут быть специализированы для разных типов символов. Классы, показанные на рис. 7.1, специализированы для значений типа char. В книге мы будем применять именно эти специализации. Если мы воспользуемся префиксом w для упомянутых имен класса, то получим названия wistream, wostream и т.д. — они являются специализациями для типа wchar_t вместо char.
В верхней части рис. 7.1 мы видим класс std::ios_base. Мы практически никогда не будем использовать непосредственно его; он приведен для полноты картины, поскольку другие классы наследуют от него. Следующая специализация — это std::ios, она воплощает идею объекта, сопровождающего поток данных, который может находиться в исправном состоянии, в состоянии, когда закончились данные (empty of data, EOF) или в другом ошибочном состоянии.
Первыми специализациями, которые мы применим на самом деле, являются std::istream и std::ostream. Префиксы "i" и "o" расшифровываются как input и output («ввод» и «вывод»). Мы уже видели такие префиксы на раннем этапе программирования на С++ в простейших примерах в объектах std::cout и std::cin (а также std::cerr). Экземпляры этих классов всегда доступны глобально. Мы выполняем вывод данных с помощью ostream, а ввод — с использованием input.
Класс, который наследует от классов istream и ostream, называется iostream. С его помощью можно выполнять как ввод, так и вывод данных. Зная, как использовать все классы из трио istream, ostream и iostream, вы сможете незамедлительно применить следующие классы:
□ классы ifstream, ofstream и fstream наследуют от классов istream, ostream и iostream соответственно, но задействуют их возможности, чтобы перенаправить ввод/вывод в файлы или из них из файловой системы компьютера;
□ классы istringstream, ostringstream и iostringstream работают по схожему принципу. Они помогают создавать строки в памяти, а затем помещают туда данные или считывают их.
Создание, конкатенация и преобразование строк
Даже те, кто довольно давно пользовался языком C++ , знают о классе std::string. Хотя в языке C обработка строк довольно