1. Потік std::cin і читання токенів оператором >>
Коли програма лише друкує, вона схожа на радіо: каже щось у порожнечу й сподівається, що хтось слухає. Але щойно зʼявляється введення, програма стає схожою на діалог: ви даєте дані, а вона відповідає. У C++ введення зазвичай виконують через std::cin. Корисно уявляти cin як потік символів: у ньому міститься все, що користувач надрукував, включно з пробілами та символами нового рядка.
Найважливіше: оператор >> читає не «все підряд», а одну порцію даних за раз. І ця «порція» майже завжди обмежена пробільними символами.
std::cin і оператор >>: читаємо значення у змінну
Щоб щось прочитати, потрібна змінна, у яку це «щось» можна покласти. Логіка така: оголосили змінну, прочитали в неї дані й використали їх далі — наприклад, вивели на екран.
Мінімальний приклад: читаємо одне ціле число.
#include <iostream>
int main() {
int age = 0;
std::cin >> age;
std::cout << "Age = " << age << '\n'; // якщо введено 20, буде: Age = 20
}
Тут важливо звернути увагу на дві речі. По‑перше, std::cin >> age; намагається прочитати наступне значення з вхідного потоку й записати його в age. По‑друге, тип змінної має значення: оскільки age — це int, програма очікує побачити у вхідному потоці число, наприклад 20, -5 або 0.
Що саме читає >>: токен і пробільні розділювачі
Зараз прозвучить слово «токен», але не лякайтеся: це просто шматочок тексту без пробілів. Оператор >> працює за таким принципом: «Пропусти пробіли, а потім прочитай поспіль символи до наступного пробілу». Пробільним символом при цьому вважається не лише ' ' — звичайний пробіл, а й символ нового рядка '\n' та табуляція '\t'.
Уявіть, що користувач увів такий рядок. Символ ⏎ — це Enter, тобто '\n':
12 34 ⏎
Між 12 і 34 тут три пробіли. Для >> це не має значення: він бачить «число», потім «пробіли», а далі — ще одне «число».
#include <iostream>
int main() {
int a = 0;
int b = 0;
std::cin >> a >> b;
std::cout << "a=" << a << ", b=" << b << '\n'; // якщо введено "12 34", буде: a=12, b=34
}
Зверніть увагу: std::cin >> a >> b; — це два послідовні читання, просто записані в одному рядку. Спочатку читається a, потім — b.
Розділювачі: пробіл, Enter і табуляція однаково «ріжуть» токени
Іноді новачкам здається, що Enter «сильніший» за пробіл або що пробіл тут «просто для краси». Для operator>> усе простіше: будь-який пробільний символ — це межа токена, і крапка.
Уявімо таке введення, де кожне слово стоїть з нового рядка:
Ivan⏎
Ivanov⏎
Або таке, де обидва слова стоять в одному рядку:
Ivan Ivanov⏎
Для коду нижче результат буде однаковим, тому що і пробіл, і Enter — це розділювачі токенів.
#include <iostream>
#include <string>
int main() {
std::string first;
std::string last;
std::cin >> first >> last;
std::cout << first << " | " << last << '\n'; // Ivan | Ivanov
}
Це добра новина: дані можна вводити як зручно — в одному рядку або в кількох. Але саме через це й виникають обмеження, про які ми поговоримо далі.
Мінісхема: як >> «ріже» введення на токени
Щоб усе це перестало здаватися магією, корисно хоча б раз побачити просту модель. Нехай користувач увів ось так:
23 Ada Lovelace⏎
Тоді потік символів можна подумки розділити на токени:
flowchart LR
A["23"] --> B["Ada"] --> C["Lovelace"]
А пробіли та символи нового рядка — це лише розділювачі, які між токенами просто пропускаються.
Практичний висновок дуже простий: якщо ви читаєте int, а потім std::string, то int «зʼїсть» перший токен, а std::string — наступний. Жодних домовленостей на кшталт «вводьте з нового рядка» на рівні >> не існує: він однаково ділить введення за пробілами.
2. Практика: мініанкета й рядки без пробілів
Практичний приклад: робимо мініанкету
Відтепер працюватимемо з тим самим мініпроєктом: консольною анкетою. Він простий, зате на ньому зручно поступово нарощувати навички: сьогодні читаємо токени, а трохи пізніше навчимося читати цілі рядки й акуратно обробляти введення.
Версія 1: читаємо вік та імʼя як одне слово.
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string name;
std::cin >> age >> name;
std::cout << "User: " << name << '\n';
std::cout << "Age: " << age << '\n';
}
Якщо ввести:
20 Ivan
то програма виведе:
User: Ivan
Age: 20
Можна ввести й так — через Enter:
20
Ivan
З погляду >> це одне й те саме: токени все одно читаються послідовно.
Введення рядків через >>: читається слово, а не фраза
Тепер ми підійшли до типового сюрпризу: std::cin >> s для std::string читає лише одне слово, тобто токен до пробілу, Enter або табуляції. Якщо ви введете кілька слів, вони не зникнуть — вони просто залишаться у вхідному потоці й будуть прочитані наступними операціями >>.
Покажемо це на нашій анкеті: прочитаємо «місто» як рядок.
#include <iostream>
#include <string>
int main() {
std::string city;
std::cin >> city;
std::cout << "City=[" << city << "]\n";
}
Якщо користувач введе:
New York
то city стане "New", а слово "York" залишиться у вхідному потоці як наступний токен. Програма виведе:
City=[New]
Це не помилка й не «кривий C++». Це звичайна логіка оператора >>: він читає один токен.
Показовий експеримент: «втрачене» слово насправді не втрачене
Слово, яке не потрапило в першу рядкову змінну, зазвичай не зникає. Воно просто чекає своєї черги. Покажімо це чесно: прочитаємо два слова у дві змінні.
#include <iostream>
#include <string>
int main() {
std::string w1;
std::string w2;
std::cin >> w1 >> w2;
std::cout << "w1=[" << w1 << "], w2=[" << w2 << "]\n";
}
Якщо ввести:
New York
отримаємо:
w1=[New], w2=[York]
Тобто >> чудово підходить для форматів, де вводяться окремі слова: імʼя, прізвище, команда, логін. Але для полів на кшталт «адреса» або «назва книги» він незручний, тому що там майже завжди є пробіли.
Памʼятка: що вважається розділювачем для >>
Коли ви почнете писати невеликі програми, то часто ловитимете себе на думці: «Чому воно зупинилося саме тут?». Зазвичай відповідь є в таблиці нижче:
| Символ у введенні | Приклад | Для >> це… | Що відбувається |
|---|---|---|---|
| пробіл ' ' | |
розділювач | рядок через >> прочитає лише Ivan |
| символ нового рядка '\n' (Enter) | |
розділювач | поведінка така сама, як і з пробілом |
| табуляція '\t' | |
розділювач | токени теж розділяються |
Висновок простий: >> читає «до найближчого пробільного символу».
Як вибрати формат введення для std::cin >>
Коли ви обираєте std::cin >>, то фактично обираєте і формат: «користувач вводить значення окремими токенами». Це ідеально підходить для завдань, де вхідні дані мають вигляд «два числа», «три числа», «слово і число», «імʼя та прізвище», «команда і параметр».
Наприклад, наша анкета добре поєднується з таким підходом, якщо ми заздалегідь домовилися, що імʼя та прізвище вводяться окремо, а не одним рядком. Тоді застосунок можна акуратно розширити: читаємо імʼя, прізвище й вік.
#include <iostream>
#include <string>
int main() {
std::string first;
std::string last;
int age = 0;
std::cin >> first >> last >> age;
std::cout << "Profile: " << first << " " << last << '\n';
std::cout << "Age: " << age << '\n';
}
Якщо ввести:
Ada Lovelace 36
то все чудово зчитається: три токени → три змінні.
Мікронюанс: >> не «зʼїдає» пробіли як дані
Коли ми кажемо «пробіл — розділювач», важливо розуміти таке: пробіли не потрапляють у рядок, який читає >>. Він читає лише значущі символи токена. Тому std::string word; std::cin >> word; ніколи не прочитає рядок "Hello world" повністю: пробіл як символ усередині токена неможливий за самим визначенням токена.
Для порівняння: якби ви хотіли читати весь рядок цілком, включно з пробілами, потрібен був би інший інструмент. Але тут ми свідомо не забігаємо наперед: поки що наше завдання — впевнено користуватися >> і не чекати від нього того, чого він не обіцяв.
3. Обмеження >>: невідповідність типу й безпечне читання тексту
Коли тип важливіший за ваші бажання: читання може «зламатися»
Друга велика група обмежень повʼязана з невідповідністю типів. Якщо ви читаєте int, а користувач вводить слово, програма не зможе коректно перетворити «котик» на число.
Покажемо сценарій, який виглядає цілком правдоподібно: людина переплутала й увела вік словами.
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string name;
std::cin >> age >> name;
std::cout << "age=" << age << ", name=" << name << '\n';
}
Якщо ввести:
twenty Ivan
то програма вже не зможе коректно прочитати age як число. Ми поки що не вміємо красиво обробляти таке введення — це буде окрема тема пізніше в курсі. Тому зараз правило просте й чесне: коли ви розвʼязуєте завдання, де формат введення задано, припускається, що користувач вводить дані правильно.
Чому ми читаємо рядки в std::string, а не в char[]
Іноді в старих прикладах із C/C++ можна побачити щось на кшталт char name[20]; std::cin >> name;. Це працює, але небезпечно: якщо користувач введе надто довге слово, можна отримати серйозні проблеми, аж до переповнення буфера.
Ми поки що не вивчали масиви й вказівники, тому тримаймося простого практичного правила: для тексту використовуйте std::string. Вона значно безпечніша як контейнер для тексту.
4. Типові помилки під час роботи з std::cin >>
Помилка № 1: очікувати, що std::cin >> s; прочитає цілу фразу.
Це одна з найчастіших пасток. Новачок вводить New York, а у змінній опиняється лише New. Причина проста: >> читає токен до пробільного розділювача. Якщо за змістом вам потрібен рядок із пробілами, то такий формат введення для >> не підходить.
Помилка № 2: не оголосити змінну до читання.
Іноді хочеться написати щось на кшталт «прочитай вік» без змінної, але std::cin не вміє читати «в нікуди». Спочатку потрібно оголосити змінну потрібного типу — int age = 0;, — а вже потім виконувати std::cin >> age;. Це не формальність: змінна — це місце в памʼяті, куди потрапить результат.
Помилка № 3: переплутати порядок читання й порядок даних.
Якщо програма виконує std::cin >> age >> name;, а користувач вводить Ivan 20, то age спробує стати Ivan, а це не число, і читання піде не за планом. У >> немає телепатії: він читає токени строго за порядком і намагається покласти їх у змінні вказаного типу.
Помилка № 4: думати, що Enter «закінчує введення для програми».
Enter лише додає символ '\n' у вхідний потік. Для >> це такий самий пробільний розділювач, як і звичайний пробіл. Тому «введення з нового рядка» і «введення через пробіл» найчастіше еквівалентні. Це зручно, але іноді ламає очікування, якщо ви подумки будували введення суворо по рядках.
Помилка № 5: вводити не той тип і дивуватися результату.
Якщо очікується int, то введення на кшталт ten не може бути коректно перетворене на число оператором >>. На цьому етапі курсу ми ще не вчимося красиво обробляти таке введення, тому найкраще рішення для навчальних завдань — уважно стежити за форматом даних і вводити значення саме в тому вигляді, у якому їх очікує програма.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ