14  }

15 }

16

17 int main(int argc, char** argv) {

18

19  if (argc < 2)

20   return(EXIT_FAILURE);

21

22  std::ifstream in(argv[1]);

23

24  if (!in)

25   exit(EXIT_FAILURE);

26

27  StrIntMap w;

28  countWords(in, w);

29

30  for (StrIntMap::iterator p = w.begin();

31   p != w.end(); ++p) {

32   std::cout << p->first << ' присутствует '

33    << p->second << ' раз. ';

34  }

35 }

Обсуждение

Пример 4.27 кажется вполне простым, но в нем делается больше, чем кажется. Большая часть тонкостей связана с map, так что вначале давайте обсудим его.

Если вы не знакомы с map, то вам стоит узнать про него, map — это шаблон класса контейнера, который является частью STL. Он хранит пары ключ-значение в порядке, определяемом std::less или вашей собственной функцией сравнения. Типы ключей и значений, которые можно хранить в нем, зависят только от вашего воображения. В этом примере мы просто сохраняем string и int.

В строке 6 я для упрощения читаемости кода использовал typedef.

typedef map<string, int> StrIntMap;

Таким образом, StrIntMap — это map, который хранит пары string/int. Каждая string — это уникальное слово именно по этой причине я использую ее как ключ, — которое было прочитано из входного потока, а связанное с ней int — это число раз, которое это слово встретилось. Все, что осталось, — это прочитать все слова по одному, добавить их в map, если их там еще нет, и увеличить значение счетчика, если они там уже есть.

Это делает countWords. Основная логика кратка.

while (in >> s) {

 ++words[s];

}

operator>> читает из левого операнда (istream) непрерывные отрезки, не содержащие пробелов, и помещает их в правый операнд (string). После прочтения слова все, что требуется сделать, — это обновить статистику в map, и это делается в следующей строке.

++words[s];

map определяет operator[], позволяющий получить значение данного ключа (на самом деле он возвращает ссылку на само значение), так что для его инкремента просто инкрементируется значение, индексируемое с помощью заданного ключа. Но здесь могут возникнуть небольшие осложнения. Что, если ключа в map еще нет? Разве мы не попытаемся увеличить несуществующий элемент, и не обрушится ли программа, как в случае с обычным массивом? Нет, map определяет operator[] не так, как другие контейнеры STL или обычные массивы.

В map operator[] делает две вещи: если ключ еще не существует, он создает значение, используя конструктор типа значения по умолчанию, и добавляет в map эту новую пару ключ/значение, а если ключ уже существует, то никаких изменений не вносится. В обоих случаях возвращается ссылка на значение, определяемое ключом, даже если это значение было только что создано конструктором по умолчанию. Это удобная возможность (если вы знаете о ее существовании), так как он устраняет необходимость проверки в клиентском коде существования ключа перед его добавлением.

Теперь посмотрите на строки 32 и 33. Итератор указывает на члены, которые называются first и second — что это такое? map обманывает вас, используя для хранения пар имя/значение другой шаблон класса: шаблон класса pair, определенный в <utility> (уже включенный в <map>). При переборе элементов, хранящихся в map, вы получите ссылки на объекты pair. Работа с pair проста. Первый элемент пары хранится в элементе first, а второй хранится, естественно, в second.

В примере 4.27 я для чтения из входного потока непрерывных фрагментов текста использую operator>>, что отличается от некоторых других примеров. Я делаю это для демонстрации того, как это делается, но вам почти наверняка потребуется изменить его поведение в зависимости от определения «слова» текстового файла. Например, рассмотрим фрагмент вывода, генерируемого примером 4.27.

with присутствует 5 раз.

work присутствует 3 раз.

workers присутствует 3 раз.

workers, присутствует 1 раз.

years присутствует 2 раз.

years, присутствует 1 раз.

Обратите внимание, что точки в конце слов рассматриваются как части слов. Скорее всего, вам потребуется с помощью функций проверки символов из <cctype> и <cwctype> изменить определение слова так, чтобы оно означало только буквенно- цифровые символы, как это сделано в рецепте 4.17.

Смотри также

Рецепт 4.17 и табл. 4.3.

4.19. Добавление полей в текстовый файл

Проблема

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

Решение

Пример 4.28 показывает, как добавить в файл поля с помощью потоков, string и шаблона функции getline.

Пример 4.28. Добавление полей в текстовый файл

#include <iostream>

#include <fstream>

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату