
- •Лекція 1
- •Зауваження до програми
- •Правила ініціалізації масивів
- •Лекція 2
- •Конкатенація рядкових літералів
- •Використання рядків у масивах
- •Зауваження до програми
- •Ризики, пов'язані з введенням рядків
- •Читання введення по рядках
- •Введення, орієнтоване на рядки за допомогою getline()
- •Введення, орієнтоване на рядки за допомогою get()
- •Змішування рядкового та числового вводу
- •Клас string
- •Лекція 3
- •Використання структур в програмах
- •Зауваження до програми
- •Чи може структура містити член типу string?
- •Інші властивості структур
- •Масиви структур
- •Лекція 4
- •Перерахування
- •Встановлення значень нумераторам
- •Діапазони значень нумераторів
- •Лекція 5
- •Оголошення та ініціалізація вказівників
- •Небезпека пов’язана з вказівниками
Читання введення по рядках
Читання рядка по одному слову за раз - часто не є бажаною поведінкою. Наприклад, припустимо, що програма запитує у користувача місто, і користувач відповідає введенням New York або Sao Paulo. Ви б хотіли, щоб програма прочитала і зберегла повні назви, а не тільки New і Sao. Щоб мати можливість вводити цілі фрази замість окремих слів, необхідний інший підхід до введення рядків. Точніше кажучи, потрібен метод, орієнтований на рядки, замість методу, орієнтованого на слова. На щастя, у класу istream, екземпляром якого є cin, є функції-члени, призначені для введення, орієнтованого на рядки: getline() і get(). Обидва читають весь рядок введення - тобто аж до символу нового рядка. Однак getline() потім відкидає символ нового рядка, в той час як get() залишає його у вхідній черзі. Давайте розглянемо їх детально, починаючи з getline().
Введення, орієнтоване на рядки за допомогою getline()
Функція getline() читає цілий рядок, використовуючи символ нового рядка, який передано клавішею <Enter>, для позначення кінця введення. Цей метод ініціюється викликом функції cin.getline(). Функція приймає два аргументи. Перший аргумент - це ім'я місця призначення (тобто масиву, який зберігає введений рядок), а другий - максимальна кількість символів, що підлягають читанню. Якщо, скажімо, встановлена межа 20, то функція читає не більше 19 символів, залишаючи місце для нульового символу, що додається автоматично. Функція-член getline() припиняє читання, коли досягає вказаної межі кількості символів або коли читає символ нового рядка - дивлячись, що відбудеться раніше.
Наприклад, припустимо, що ви хочете скористатися getline() для читання імені в 20-елементний масив name. Для цього слід вказати такий виклик:
Він читає весь рядок в масив name, припускаючи, що рядок складається не більше ніж з 19 символів.
У лістингу 4 представлений модифікований приклад з лістингу 3 із застосуванням cin.getline() замість простого cin. В іншому програма залишилась колишньою.
Приклад виконання програми з лістингу 4:
Тепер програма читає повне ім'я та назву страви. Функція getline() зручним чином приймає по одному рядку за раз. Вона читає введення до нового символу рядка, позначаючи кінець рядка, але не зберігаючи при цьому сам символ нового рядка. Замість цього вона замінює його нульовим символом при збереженні рядка.
Введення, орієнтоване на рядки за допомогою get()
Тепер спробуємо інший підхід. Клас istream має функцію-член get(), яка доступна в різних варіантах. Один з них працює майже так само, як getline(). Він приймає ті ж аргументи, інтерпретує їх аналогічним чином, і читає до кінця рядка. Але замість того, щоб прочитати і відкинути символ нового рядка, get() залишає його у вхідній черзі. Припустимо, що використовуються два виклики get() підряд:
Оскільки перший виклик залишає символ нового рядка у вхідній черзі, виходить, що символ нового рядка виявляється першим символом, який бачить наступний виклик. Таким чином, другий виклик get() встановлює, що він досяг кінця рядка, не знайшовши нічого цікавого, що можна було б прочитати. Без сторонньої допомоги get() взагалі не може подолати цей символ нового рядка.
На щастя, на допомогу приходять різні варіанти get(). Виклик з in.get() без аргументів читає одиночний наступний символ, навіть якщо це буде символ нового рядка, тому можна використовувати його для того, щоб відкинути символ нового рядка і підготуватися до введення наступного рядка. Тобто наступна послідовність буде працювати правильно:
Інший спосіб застосування get() полягає в конкатенації, або з'єднанні, двох викликів функцій-членів класу:
Таку можливість забезпечує те, що cin.get(name, ArSize) повертає об'єкт cin, який потім використовується як об'єкт, що викликає функцію get(). Аналогічно оператор читає два наступних один за одним рядки в масиви name1 і name2, що еквівалентно двом окремим викликам cin.getline():
В лістингу 5 використовується конкатенація:
Приклад запуску програми:
Зверніть увагу на те, що C++ допускає існування безлічі версій функції з різними списками аргументів.
Якщо ви використовуєте, скажімо, cin.get(name, ArSize), то компілятор визначає, що викликається форма, яка поміщає рядок в масив, і підставляє відповідну функцію-член. Якщо ж замість цього ви застосовуєте cin.get(), то компілятор бачить, що вам потрібна форма, яка читає один символ. Такий засіб називається перевантаженням функцій.
Навіщо взагалі може знадобитися викликати get() замість getline()? По-перше, старі реалізації можуть не містити getline(). По-друге, get() дозволяє виявляти більшу обережність. Припустимо, наприклад, що ви скористалися get(), щоб прочитати рядок в масив. Як ви визначите, був прочитаний повний рядок або ж читання перервалося у зв'язку із заповненням масиву? Для цього потрібно подивитися на наступний у черзі символ. Якщо це символ нового рядка, значить, був прочитаний весь рядок. В іншому випадку рядок був прочитаний не повністю і ще є що читати. Коротше кажучи, getline() трохи простіше в застосуванні, але get() спрощує перевірку помилок.
Порожні рядки та інші проблеми
Що відбувається після того, як функції getline() і get() прочитали порожній рядок? Спочатку передбачалося, що наступний оператор введення повинен отримати вказівку, де завершив роботу попередній виклик getline() або get(). Однак сучасна практика полягає в тому, що після того, як функція get() (але не getline()) прочитає порожній рядок, вона встановлює прапорець, який називається failbit. Вплив цього прапорця полягає в тому, що подальше введення блокується, але його можна відновити наступною командою:
Інша потенційна проблема пов'язана з тим, що вхідні рядки можуть бути довшими, ніж виділений для них простір. Якщо вхідні рядки довші, ніж вказана кількість символів, то і getline(), і get() залишають надлишкові символи у вхідній черзі. Однак getline() додатково встановлює failbit і відключає подальше введення.