📚 Hub Books: Онлайн-чтение книгРазная литератураC++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+
1 ... 67 68 69 70 71 72 73 74 75 ... 121
Перейти на страницу:
можно собрать сложные структуры из потока ввода, а затем заполнить списки или векторы полученными элементами.

В этот раз мы усложним задачу, заполняя на основе стандартного потока ввода контейнер std::map. Проблема заключается в том, что мы не можем просто заполнить отдельную структуру значениями и отправить ее в линейный контейнер наподобие списка или вектора, поскольку в ассоциативных массивах полезная нагрузка распределяется между ключом и значением. Однако, как вы увидите, само решение задачи отличается незначительно.

После изучения этого примера вы сможете легко выполнять сериализацию и десериализацию сложных структур данных.

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

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

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

#include <iostream>

#include <iomanip>

#include <map>

#include <iterator>

#include <algorithm>

#include <numeric>

using namespace std;

2. Мы хотим создать небольшую базу данных интернет-мемов. Предположим, мем имеет название, описание и год, в который он родился или был создан. Сохраним их в контейнере std::map, где название выступит в качестве ключа, а другая информация будет помещена в структуру как значение, связанное с ним:

struct meme {

  string description;

  size_t year;

};

3. Сначала проигнорируем ключ и просто реализуем перегруженную функцию потокового оператора >> для структуры meme. Предположим, что описание мема окружено кавычками, за ним следует год. Это будет выглядеть так: "some description" 2017. Описание окружено кавычками, так что может содержать пробелы, поскольку мы знаем, что все символы, стоящие между кавычками, принадлежат описанию. После чтения с помощью конструкции is >> quoted(m.description) кавычки автоматически используются как разделители и впоследствии отбрасываются, что очень удобно. Сразу после этого считываем число, которое представляет собой год:

istream& operator>>(istream &is, meme &m) {

  return is >> quoted(m.description) >> m.year;

}

4. О’кей, теперь примем в расчет название мема в качестве ключа ассоциативного массива. Чтобы вставить мем в массив, нужно создать объект типа std::pair<тип_ключа, тип_значения>. Типом ключа является string, а типом значения — meme. В названии мема также могут находиться пробелы, поэтому мы используем ту же оболочку, которую применяли для описания. p.first — это название, а p.second — целая структура meme, связанная с ним. Данный объект будет передан другой реализации оператора >>, которую мы только что создали:

istream& operator >>(istream &is,

                     pair<string, meme> &p) {

  return is >> quoted(p.first) >> p.second;

}

5. На этом все. Напишем функцию main, в которой будет создаваться ассоциативный массив, и заполним его. Поскольку мы переопределили оператор >>, итератор istream_iterator может работать с данным типом непосредственно. Мы позволим ему десериализовать наши объекты типа meme, полученные из стандартного потока ввода, и используем итератор вставки, чтобы поместить их в ассоциативный массив:

int main()

{

  map<string, meme> m;

  copy(istream_iterator<pair<string, meme>>{cin},

       {},

       inserter(m, end(m)));

6. Прежде чем вывести на экран все, что у нас есть, сначала найдем самое длинное название мема в ассоциативном массиве. Для этого воспользуемся алгоритмом std::accumulate. Он получит исходное значение 0u (u расшифровывается как unsigned — «беззнаковый») и пройдет по всем элементам ассоциативного массива, чтобы слить их воедино. С точки зрения алгоритма accumulate слияние обычно означает сложение. В нашем случае требуется не численная сумма, а размер самой длинной строки. Для получения этого значения предоставим алгоритму вспомогательную функцию max_func, которая принимает переменную, содержащую текущий максимальный размер строки (она должна быть беззнаковой, поскольку длина строки знака не имеет), и сравнивает ее с длиной названия текущего мема, чтобы взять максимальное значение. Это делается для каждого элемента. Итоговое возвращаемое значение функции accumulate представляет собой максимальную длину названия мема:

  auto max_func ([](size_t old_max,

                    const auto &b) {

    return max(old_max, b.first.length());

  });

  size_t width {accumulate(begin(m), end(m),

                           0u, max_func)};

7. Теперь быстро пройдем по ассоциативному массиву и выведем каждый элемент. Чтобы выходные данные смотрелись более «аккуратно», воспользуемся конструкцией << left << setw(width):

  for (const auto &[meme_name, meme_desc] : m) {

    const auto &[desc, year] = meme_desc;

    cout << left << setw(width) << meme_name

         << " : " << desc

         << ", " << year << 'n';

  }

}

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

"Doge" "Very Shiba Inu. so dog. much funny. wow." 2013

"Pepe" "Anthropomorphic frog" 2016

"Gabe" "Musical dog on maximum borkdrive" 2016

"Honey Badger" "Crazy nastyass honey badger" 2011

"Dramatic Chipmunk" "Chipmunk with a very dramatic look" 2007

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

$ cat memes.txt | ./filling_containers

Doge              : Very Shiba Inu. so dog. much funny. wow., 2013

Dramatic Chipmunk : Chipmunk with a very dramatic look, 2007

Gabe              : Musical dog on maximum borkdrive, 2016

Honey Badger      : Crazy nastyass honey badger, 2011

Pepe              : Anthropomorphic frog, 2016

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

В данном примере можно отметить три интересные детали. Первая заключается в том, что на основе данных последовательного символьного потока ввода мы заполняли не вектор или список, а более сложный контейнер std::map. Еще одна — мы использовали эти манипуляторы потока. Последняя — вызов accumulate, который определяет размер самой длинной строки.

Начнем с работы с ассоциативным массивом. Наша структура meme содержит только поле с описанием и год. Название интернет-мема не является частью структуры, поскольку используется в качестве ключа. При вставке чего-нибудь в ассоциативный массив можно предоставить объект типа std::pair, имеющий разные типы для ключа и значения. Именно это мы и сделали. Сначала реализовали оператор потока >> для структуры meme, а затем сделали то же самое для типа pair<string,

1 ... 67 68 69 70 71 72 73 74 75 ... 121
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?