Бібліотека iostream досить складна, тому ми не зможемо охопити її повністю в рамках даних уроків. Проте, ми можемо розглянути її основний функціонал. На цьому уроці ми розберемося з класом istream
.
Примітка: Весь функціонал об’єктів, які працюють з потоками вводу/виводу, знаходиться в просторі імен std. Це означає, що вам потрібно або додавати префікс std::
до всіх об’єктів і функцій вводу/виводу, або використовувати рядок using namespace std;
.
Оператор вилучення
Як ви вже дізналися на попередньому уроці, ми можемо використовувати оператор вилучення >>
для зчитування інформації з вхідного потоку. Однією з найбільш поширених проблем при зчитуванні рядків з вхідного потоку є запобігання переповнення. Наприклад:
1 2 3 4 5 6 7 |
#include <iostream> int main() { char buf[12]; std::cin >> buf; } |
Що відбудеться, якщо користувач введе 20 символів? Правильно, переповнення. Одним із способів вирішення цієї проблеми є використання маніпуляторів. Маніпулятор — це об’єкт, який застосовується для зміни потоку даних з використанням операторів вилучення (>>
) або вставки (<<
).
Ми вже працювали з одним з маніпуляторів — endl, який одночасно виводить символ нового рядка і видаляє поточні дані з буфера. Мова C++ надає ще один маніпулятор — setw() (з заголовку iomanip), який використовується для обмеження кількості символів, які зчитуються з потоку. Для використання setw() вам потрібно просто передати в якості параметра максимальну кількість символів для вилучення і вставити виклик цього маніпулятора наступним чином:
1 2 3 4 5 6 7 8 |
#include <iostream> #include <iomanip> int main() { char buf[12]; std::cin >> std::setw(12) >> buf; } |
Ця програма тепер прочитає тільки перші 11 символів з вхідного потоку (+ один символ для нуль-термінатора). Всі інші символи залишаться в потоці до наступного вилучення.
Вилучення і пробіли
Важливий момент: оператор вилучення працює з «відформатованими» даними, тобто він ігнорує всі пробіли, символи табуляції і символ нового рядка. Наприклад:
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { char ch; while (std::cin >> ch) std::cout << ch; return 0; } |
Якщо користувач введе наступне:
Hello! My name is Anton
То оператор вилучення пропустить всі пробіли і символи нового рядка. Отже, результат виконання програми:
Hello!MynameisAnton
Часто користувацький ввід все ж потрібен з усіма його пробілами. Для цього клас istream
надає безліч функцій. Однією з найбільш корисних є функція get(), яка вилучає символ з вхідного потоку. Ось вищенаведена програма, але вже з використанням функції get():
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { char ch; while (std::cin.get(ch)) std::cout << ch; return 0; } |
Тепер, якщо ми введемо наступне:
Hello! My name is Anton
То отримаємо:
Hello! My name is Anton
Функція get() також має рядкову версію, в якій можна вказати максимальну кількість символів для вилучення. Наприклад:
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { char strBuf[12]; std::cin.get(strBuf, 12); std::cout << strBuf << std::endl; return 0; } |
Якщо ми введемо наступне:
Hello! My name is Anton
То отримаємо:
Hello! My n
Зверніть увагу, програма зчитує тільки перші 11 символів (+ нуль-термінатор). Решта символів залишаються у вхідному потоці.
Один важливий нюанс: функція get() НЕ зчитує символ нового рядка! Наприклад:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { char strBuf[12]; // Зчитуємо перші 11 символів std::cin.get(strBuf, 12); std::cout << strBuf << std::endl; // Зчитуємо додатково ще 11 символів std::cin.get(strBuf, 12); std::cout << strBuf << std::endl; return 0; } |
Якщо користувач введе наступне:
Hello!
То отримає:
Hello!
І програма відразу ж завершить своє виконання! Чому так? Чому не спрацює другий ввід даних? Справа в тому, що перший get() зчитує символи до символу нового рядка, а потім зупиняється. Другий get() бачить, що у вхідному потоці все ще є дані і намагається їх отримати. Але перший символ, на який він натикається — символ нового рядка, тому відбувається другий «Стоп!».
Для вирішення даної проблеми клас istream
надає функцію getline(), яка працює точно так само, як і функція get(), але при цьому може зчитувати символи нового рядка:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { char strBuf[12]; // Зчитуємо 11 символів std::cin.getline(strBuf, 12); std::cout << strBuf << std::endl; // Зчитуємо додатково ще 11 символів std::cin.getline(strBuf, 12); std::cout << strBuf << std::endl; return 0; } |
Цей код працює точно так, як очікується, навіть якщо користувач введе рядок з символом нового рядка.
Якщо вам потрібно дізнатися кількість символів, вилучених останнім getline(), використовуйте функцію gcount():
1 2 3 4 5 6 7 8 9 10 11 |
#include <iostream> int main() { char strBuf[100]; std::cin.getline(strBuf, 100); std::cout << strBuf << std::endl; std::cout << std::cin.gcount() << " characters were read" << std::endl; return 0; } |
Результат:
Hello! My name is Anton
24 characters were read
Спеціальна версія функції getline() для std::string
Є спеціальна версія функції getline(), яка знаходиться поза класом istream
і використовується для зчитування змінних типу std::string. Вона не є членом ні ostream
, ні istream
, а підключається заголовком string. Наприклад:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> #include <string> int main() { using namespace std; string strBuf; getline(cin, strBuf); cout << strBuf << endl; return 0; } |
Ще декілька корисних функцій класу istream
Є ще кілька корисних функцій класу istream
, які ви можете використовувати:
функція ignore() — ігнорує перший символ з потоку;
функція ignore(int nCount) — ігнорує перші nCount
(кількість) символів з потоку;
функція peek() — зчитує символ з потоку, при цьому не видаляючи його з потоку;
функція unget() — повертає останній зчитаний символ назад в потік, щоб його можна було вилучити в наступний раз;
функція putback(char ch) — поміщає обраний вами символ назад в потік, щоб його можна було вилучити наступного разу.
Клас istream
містить ще безліч інших корисний функцій і їх варіацій, але це вже тема для окремого уроку.