1. Знайомство з std::getline
Якщо ви щойно опанували std::cin >>, може здатися, що він уміє все: вводите слово, вводите число — і все працює. Але лише доти, доки користувач не введе фразу, ПІБ, назву книжки або адресу, тобто текст із пробілами. І тут >> раптом поводиться як надто буквальний робот: бачить пробіл і вирішує, що «ну все, слово закінчилося, я йду».
Уявіть, що std::cin >> — це турнікет у метро: він пропускає рівно одного пасажира, тобто один токен, і зачиняється на першій же «перешкоді» — пробілі чи '\n'. А std::getline — це двері до вагона: ви проходите повністю, з усіма сумками-пробілами, і ніхто не відтинає вам прізвище посередині.
Як працює std::getline
Коли потрібно прочитати увесь рядок, разом із пробілами, вам знадобиться std::getline. Це функція стандартної бібліотеки, яку зазвичай використовують зі std::string. Важливий момент: рядок зчитується до символу переведення рядка '\n', але сам '\n' до результату не потрапляє — він лише позначає межу, а не є частиною тексту.
Мінімальний шаблон має такий вигляд:
#include <iostream>
#include <string>
int main() {
std::string line;
std::getline(std::cin, line);
std::cout << "Line: " << line << '\n';
}
Тут є кілька справді важливих моментів.
По-перше, std::getline може прочитати навіть порожній рядок. Це не помилка і не ознака того, що «щось зламалося», а цілком коректне введення.
По-друге, std::getline не боїться пробілів: якщо користувач уведе Ivan Ivanov, рядок так і залишиться Ivan Ivanov, а не скоротиться до одного Ivan.
Щоб чіткіше побачити межі, особливо коли рядок порожній, зручно виводити його в квадратних дужках:
#include <iostream>
#include <string>
int main() {
std::string line;
std::getline(std::cin, line);
std::cout << "[" << line << "]\n"; // наприклад: [Hello world]
}
2. Порожні рядки та як їх помітити
Порожній рядок — це ситуація, коли користувач просто натиснув Enter і не ввів жодного символу. Новачки часто сприймають це як «getline не працює», але насправді це такий самий рядок, лише завдовжки 0. У програмуванні порожнеча — теж дані. Іноді навіть найчесніші.
Розгляньмо приклад, який наочно покаже довжину рядка. Так ви точно відрізните «порожньо» від «нічого не прочиталося»:
#include <iostream>
#include <string>
int main() {
std::string line;
std::getline(std::cin, line);
std::cout << "Text=[" << line << "]\n";
std::cout << "Size=" << line.size() << '\n'; // Size=0 для порожнього рядка
}
Зверніть увагу: line.size() повертає кількість символів. Якщо користувач увів Hi, розмір буде 2. Якщо ж він просто натиснув Enter, буде 0.
Порожні рядки можуть зʼявлятися у двох основних сценаріях.
- Перший — користувач справді ввів порожній рядок, тобто просто натиснув Enter.
- Другий — підступніший: ви змішали std::cin >> і std::getline, а getline зчитав символ переведення рядка, що залишився у потоці.
Це ніби ви прийшли по піцу, а вам винесли коробку… але порожню, бо піцу вже хтось зʼїв на попередній лекції. Зараз розберемо саме цей сценарій.
3. Пастка після std::cin >>: залишок переведення рядка
Коли std::cin >> читає токен, він бере символи до найближчого пробілу, табуляції або переведення рядка. Важлива деталь: роздільник, наприклад '\n', при цьому не зникає, а лишається у вхідному потоці. Тобто, умовно кажучи, «курсор читання» зупиняється просто перед '\n'.
А std::getline якраз працює за принципом «читаю до '\n'». Якщо наступний непрочитаний символ у введенні — це одразу '\n', то getline думає: «О, кінець рядка вже тут», і повертає порожній рядок.
Подивімося на типовий сценарій, який часто збиває з пантелику:
- користувач вводить вік 25 і натискає Enter
- потім вводить імʼя Ivan Ivanov і натискає Enter
У вхідних даних це виглядає приблизно так. Символ \n тут означає натискання Enter:
25\n
Ivan Ivanov\n
Тепер код:
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string name;
std::cin >> age;
std::getline(std::cin, name); // пастка!
std::cout << "Age=" << age << '\n';
std::cout << "Name=[" << name << "]\n"; // часто буде порожньо: []
}
Дуже ймовірний результат буде таким:
Age=25
Name=[]
І це не «поганий C++», а просто наслідок того, що '\n' після числа залишився у введенні, і getline одразу його «зʼїв» як кінець рядка.
Міні‑схема: що відбувається у вхідному потоці
Уявімо введення як стрічку, а читання — як вказівник на поточну позицію:
flowchart TD
A["Введення: 25, потім Ivan Ivanov"] --> B["cin >> age читає 25"]
B --> C["У потоці лишаються символ переведення рядка та Ivan Ivanov"]
C --> D["getline бачить першим символом переведення рядка"]
D --> E["getline повертає порожній рядок"]
Ось і вся «магія» — насправді це просто механіка. І добра новина в тому, що виправляється вона таким самим простим механічним прийомом.
4. Як змішувати >> і getline
Коли за форматом введення вам потрібно спочатку прочитати щось через std::cin >>, наприклад число, а потім повністю зчитати рядок через std::getline, найпростіший спосіб зробити поведінку стабільною — виконати ще один виклик getline у змінну-заглушку. Так ви заберете залишок поточного рядка, зазвичай той самий '\n'.
Ось класичний прийом:
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string dummy;
std::string name;
std::cin >> age;
std::getline(std::cin, dummy); // забрали залишений '\n'
std::getline(std::cin, name); // тепер читаємо реальний рядок
std::cout << "Age=" << age << '\n';
std::cout << "Name=[" << name << "]\n";
}
Тепер, якщо користувач вводить:
25
Ivan Ivanov
то виведення стане очікуваним:
Age=25
Name=[Ivan Ivanov]
Чому це працює? Тому що «перехідний» getline зчитує все, що лишилося після >>, до кінця поточного рядка. Найчастіше це один-єдиний '\n', але той самий принцип працює й у складніших випадках, наприклад якщо після числа були пробіли.
5. Кілька getline підряд
Коли ви вже перейшли на std::getline, зчитувати кілька рядків підряд стає дуже зручно: кожен рядок — це окремий виклик. І тут уже немає проблеми «залишку '\n'», бо getline сам акуратно читає до '\n' і залишає потік на початку наступного рядка.
Уявімо, що ми хочемо прочитати «місто» і «вулицю» — обидва рядки можуть містити пробіли:
#include <iostream>
#include <string>
int main() {
std::string city;
std::string street;
std::getline(std::cin, city);
std::getline(std::cin, street);
std::cout << "City=[" << city << "]\n";
std::cout << "Street=[" << street << "]\n";
}
Якщо користувач уведе:
New York
5th Avenue
то програма чесно прочитає обидва рядки повністю.
Тут важливо зрозуміти різницю: якби ви спробували зробити це через std::cin >>, то New York перетворився б на New, а York «поїхав» би в наступну змінну. Іноді це корисно, коли формат суворо токенний, але для звичайного людського тексту частіше потрібен саме getline.
6. Міні‑застосунок «Анкета»
Напишімо невеликий консольний застосунок у стилі «анкети»: програма ставить запитання й виводить те, що користувач увів. Сьогодні ми трохи його вдосконалимо: навчимося читати ПІБ повністю, а не лише перше слово.
Спочатку — версія, яка виглядає логічно, але працює не зовсім так, як очікується. Покажемо типову пастку, щоб потім її було легко обійти:
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string fullName;
std::cout << "Enter age: ";
std::cin >> age;
std::cout << "Enter full name: ";
std::getline(std::cin, fullName); // часто порожньо
std::cout << "Age=" << age << '\n';
std::cout << "Full name=[" << fullName << "]\n";
}
Якщо введення таке:
25
Ivan Ivanov
то fullName часто буде порожнім. І користувач дивитиметься на вас так, ніби ви попросили ввести імʼя — і тут же його проігнорували. Незручно.
Тепер зробімо правильну версію з getline:
#include <iostream>
#include <string>
int main() {
int age = 0;
std::string dummy;
std::string fullName;
std::cout << "Enter age: ";
std::cin >> age;
std::getline(std::cin, dummy); // зʼїли '\n' після числа
std::cout << "Enter full name: ";
std::getline(std::cin, fullName);
std::cout << "Age=" << age << '\n'; // Age=25
std::cout << "Full name=[" << fullName << "]\n"; // Full name=[Ivan Ivanov]
}
Тепер це вже схоже на програму, якій можна без сорому довірити введення даних — принаймні якщо користувач вводить те, що очікується.
Маленький бонус: довжина рядка як «датчик»
Іноді, особливо під час навчання, корисно додати такий собі «датчик»:
#include <iostream>
#include <string>
int main() {
std::string line;
std::getline(std::cin, line);
std::cout << "Line=[" << line << "]\n";
std::cout << "Len=" << line.size() << '\n'; // Len=...
}
Так ви буквально бачите, порожній рядок чи ні. У реальних програмах так зазвичай не роблять, але для налагодження й навчання це дуже допомагає.
7. Типові помилки під час роботи з std::getline
Помилка № 1: очікувати, що std::getline «не може» повернути порожній рядок.
Новачки часто думають: «Раз я викликав getline, значить користувач щось увів». Але користувач міг одразу натиснути Enter, і тоді рядок чесно буде порожнім. Це не збій, а коректні дані, і програма має бути готова до того, що line.size() дорівнюватиме 0.
Помилка № 2: змішувати std::cin >> і std::getline без перехідного кроку.
Класика жанру: зчитали число через >>, потім одразу викликали getline — і отримали порожній рядок. Причина в тому, що після токенного читання у введенні лишається '\n', який getline сприймає як «кінець рядка вже зараз». Виправлення просте: додатковий getline у змінну-заглушку.
Помилка № 3: не показувати межі рядка під час налагодження й потім сперечатися з реальністю.
Коли рядок порожній, виведення std::cout << line; виглядає так, ніби «нічого не сталося». Дуже легко почати підозрювати компілятор, IDE, фази Місяця й сусідський Wi‑Fi. Насправді достатньо виводити рядок як "[" + line + "]", щоб одразу побачити: рядок справді порожній. А це вже привід розбиратися з введенням.
Помилка № 4: плутати '\n' і "\\n".
'\n' — це один символ переведення рядка, який справді зʼявляється, коли ви натискаєте Enter, і який getline використовує як межу. А "\\n" — це два символи: зворотний слеш і літера n, тобто просто текст \n. Якщо їх переплутати, можна довго не розуміти, чому «я ж бачив \n», а getline поводиться інакше.
Помилка № 5: думати, що std::getline — це «покращений >>» і читати ним усе підряд без розуміння формату.
getline справді зручний для людського тексту, але він читає «до кінця рядка». Якщо за умовою задачі введення розбито на токени, наприклад числа через пробіл, getline може бути не тим інструментом. Гарний стиль — спочатку зрозуміти формат введення, а вже потім обирати між >> і getline.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ