chitay-knigi.com » Разная литература » C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 64 65 66 67 68 69 70 71 72 ... 121
Перейти на страницу:
carl и frank? Они пропадают, поскольку мы использовали ws. 

Подсчитываем все слова в файле

Предположим, мы считали текстовый файл и хотим определить количество слов в тексте. Словом, мы называем диапазон символов, расположенный между пробелами. Как же решить эту задачу?

Например, можно подсчитать количество пробелов, поскольку между словами должны быть пробелы. В предложении "John has a funny little dog." пять символов пробела, поэтому можно сказать, что оно состоит из шести слов.

Но как быть с предложением, содержащим разные виды пробелов, например: "John has t anfunny little dog."? В нем содержится слишком много ненужных пробелов и не только. Из других примеров данной книги вы уже знаете, как удалить эти лишние пробелы. Так что сначала можно было бы предварительно обработать строку, преобразовав ее в обычное предложение, а затем применить стратегию подсчета пробелов. Это выполнимо, но существует гораздо более простой способ. Почему бы не воспользоваться теми возможностями, которые предоставляет STL?

Помимо нахождения элегантного решения этой проблемы, мы позволим пользователю решать, для каких именно входных данных считать количество слов — для данных из стандартного потока ввода или из текстового файла.

Как это делается

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

1. Включим все необходимые заголовочные файлы и объявим об использовании пространства имен std:

#include <iostream>

#include <fstream>

#include <string>

#include <algorithm>

#include <iterator>

using namespace std;

2. Функция wordcount принимает поток ввода, например cin. Она создает итератор std::input_iterator, который токенизирует строки потока, а затем передает их в std::distance. Параметр distance принимает в качестве аргументов два итератора и пытается определить, сколько именно операций инкремента нужно выполнить, чтобы переместиться с одной позиции итератора в другую. Для итераторов с произвольным доступом это делается легко, поскольку они реализуют операцию математической разности (operator-). Такие итераторы можно вычитать друг из друга, как и другие указатели. Итератор istream_iterator, однако, является однонаправленным, и его нужно сдвигать вперед до тех пор, пока он не станет равен итератору end. В конечном счете количество шагов будет равно количеству слов.

template <typename T>

size_t wordcount(T &is)

{

  return distance(istream_iterator<string>{is}, {});

}

3. В нашей функции main мы позволяем пользователю выбрать, откуда придет поток ввода — из std::cin или из файла:

int main(int argc, char **argv)

{

  size_t wc;

4. Если пользователь запустит программу в оболочке и укажет имя файла (например, $ ./count_all_words some_textfile.txt), то мы сможем получить его из параметра командной строки argv и открыть, чтобы передать новый поток ввода в функцию wordcount:

  if (argc == 2) {

    ifstream ifs {argv[1]};

    wc = wordcount(ifs);

5. В случае запуска пользователем программы без параметров мы предполагаем, что входные данные появятся из стандартного потока ввода:

  } else {

    wc = wordcount(cin);

  }

6. На этом все, просто выведем количество слов, сохраненное в переменной wc:

  cout << "There are " << wc << " wordsn";

};

7. Скомпилируем и запустим программу. Сначала передадим данные из стандартного потока ввода. Можно либо выполнить вызов echo, передав туда несколько слов, либо запустить программу и ввести несколько слов с клавиатуры. Во втором случае можно прервать ввод нажатием комбинации клавиш Ctrl+D. Так выглядит вызов echo:

$ echo "foo bar baz" | ./count_all_words

There are 3 words

8. Если запустить программу и передать ей в качестве входного файла ее файл с исходным кодом, то она подсчитает количество слов, которые содержатся в нем:

$ ./count_all_words count_all_words.cpp

There are 61 words

Как это работает

Здесь особо нечего добавить, большую часть программы мы уже объяснили при реализации, поскольку сама программа очень короткая. Единственный факт, который можно немного пояснить: std::cin и std::ifstream взаимозаменяемы. cin имеет тип std::istream, а std::ifstream наследует от std::istream. Взгляните на диаграмму наследования, приведенную в начале этой главы (см. рис. 7.1). Таким образом, они полностью взаимозаменяемы, даже во время работы программы.

 

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

Форматируем ваши выходные данные с помощью манипуляторов потока ввода-вывода

Во многих случаях недостаточно просто вывести строки и числа. Иногда числа нужно вывести в десятичной системе счисления, иногда — в шестнадцатеричной, а иногда — в восьмеричной. В одних ситуациях перед шестнадцатеричными числами префикс "0x" нужен, а в других — нет.

При выводе чисел с плавающей точкой существует множество моментов, на которые можно повлиять. Нужно ли всегда выводить их с одинаковой точностью? Надо ли выводить их вообще? Или, может быть, требуется научное представление?

Помимо представления и системы счисления нужно также представить пользователю выходные данные в «аккуратном» виде. Некоторые выходные данные можно поместить, например, в таблицы, чтобы сделать их максимально читабельными.

Все это можно сделать с помощью потоков вывода. Некоторые из этих настроек важны и при преобразовании входных значений. В данном примере мы познакомимся с так называемыми манипуляторами ввода-вывода. Часть их могут показаться сложными, так что рассмотрим их подробнее.

Как это делается

В этом примере мы выведем на экран числа, используя совершенно разные настройки форматов, чтобы ознакомиться с манипуляторами ввода-вывода.

1. Сначала включим все необходимые заголовочные файлы и объявим об использовании пространства имен std:

#include <iostream>

#include <iomanip>

#include <locale>

using namespace std;

2. Далее определим вспомогательную функцию, выводящую на экран целое число в разных стилях. Она принимает ширину отступа и символ-заполнитель, которым по умолчанию является ' ':

void print_aligned_demo(int val,

                        size_t width,

                        char fill_char = ' ')

{

3. С помощью setw можно задать минимальное количество символов, которое нужно вывести. Например, при вводе числа 123 с шириной 6 получим "

1 ... 64 65 66 67 68 69 70 71 72 ... 121
Перейти на страницу:

Комментарии
Минимальная длина комментария - 25 символов.
Комментариев еще нет. Будьте первым.