- •Буферизація
- •Потоки і буфери
- •Стандартні об'єкти введення-виводу
- •Лістинг 3. Введення рядка значень
- •Результат:
- •Введення рядків із стандартного пристрою введення
- •Результат:
- •Використання функції cout.Width()
- •Результат:
- •Лістинг 15. Вывод данных с помощью фцнкции printf()
- •Результат:
- •Лістинг 16. Відкриття файлу для читання і запису
- •Результат:
- •Налаштування відкриття файлу об'єктом ofstream
- •Лістинг 17. Додавання даних в кінець файлу
- •Результат:
- •Двійкові і текстові файли
- •Лістинг 18. Запис класу у файл
- •Результат:
- •Установка параметрів введення-виводу за допомогою коммандной рядка
- •Лістинг 20. Використання аргументів командного рядка
- •Результат:
- •Питання і відповіді
Лекція
Тема: Потоки в мові Си++
План:
-
Що таке потоки введення-виводу і як вони використовуються
-
Як за допомогою потоків управляти введенням і виведенням даних
-
Як за допомогою потоків записувати інформацію у файл і потім прочитувати її
Знайомство з потоками
Мова програмування C++ спеціально не визначає, яким чином дані виводяться на екран або у файл або як вони прочитуються програмою. Проте ці особливості є важливою частиною роботи програміста, тому стандартна бібліотека C++ включає бібліотеку iostream, що спрощує уведення-виведення (I/O).
Завдяки виділенню операцій введення-виводу в окрему бібліотеку спрощується створення апаратний незалежної мови розробки програм для різних платформ.
Інкапсуляція
Класи iostream розглядають інформацію, що виводиться програмою на екран, як побітовий потік даних. Якщо дані виводяться у файл або на екран, то джерело потоку, як правило, міститься в програмі. Якщо ж потік направлений в протилежну сторону, дані можуть поступати з клавіатури або файлу на диску. В цьому випадку вони заносяться в змінні.
Одна з основних цілей використання потоків полягає в інкапсуляції процедури обміну даними з диском або дисплеєм комп'ютера. Сама програма працює лише з потоками, які реалізують ці процеси. Схематично ця ідея проілюстрована на рис.1.
Мал. 1. Інкапсуляція за допомогою потоків
Буферизація
Запис на диск (і у меншій мірі вивід на екран) обходиться дуже дорого. Запис даних на диск і прочитування їх з диска вимагає досить багато часу, що може надовго заблокувати виконання програми. Для вирішення цієї проблеми потоки забезпечують буферизацію. Дані спочатку записуються в буфер потоку, а після його наповнення вся інформація разом записується на диск.
Суть ідеї проілюстрована на прикладі знайомого з шкільної лави бака з водою (рис..2). Вода заливається зверху, і бак поступово наповнюється, оскільки нижній вентиль закритий.
Коли вода (дані) досягає верху, нижній вентиль автоматично відкривається і вся вода виливається (мал. 3).
Як тільки бак спустіє, нижній вентиль закривається, а верхній відкривається знов, і вода знову поступає в бак (мал. 4).
В деяких випадках необхідно, аби вода відразу ж виливалася з бака, не чекаючи його наповнення. У програмуванні така ситуація називається очищенням буфера (мал. 5).
Мал. 2. Буфер наповнюється даними, як закритий бак — водою
Мал. 3. Відкривається зливний вентиль, і вода (дані) зливається з бака
Мал. 4. Повторне наповнення бака
Мал. 5. Очищення буфера подібне до екстреного зливу води
Потоки і буфери
У C++ застосовується об'єктно-орієнтований підхід до реалізації обміну даними з потоками, що буферизують.
• Клас streambuf управляє буфером, тому його функції надають можливість наповнювати, спорожняти і очищати буфер, а також виконувати з ним інші операції.
• Клас ios є базовим для класів потоків введення-виводу. Як змінна-член класу ios виступає об'єкт streambuf.
• Класи istream і ostream є похідними від класу ios і відповідають відповідно за потокове введення і виведення даних.
• Клас iosteam є похідним від класів istream і ostream і забезпечує методи введення-виводу для друку на екран.
• Класи fstream використовуються для введення-виводу з файлів.
Стандартні об'єкти введення-виводу
При запуску програми, що включає класи iostreams, створюються і ініціюються чотири об'єкти.
Примітка: Бібліотека класу iostream вбудована в компілятор. Аби додати в свою програму методи цього класу, досить в перших рядках програми включити вираження #include<iostream>.
• Об'єкт cin (виголошується як "си-ин" від англійського "see-in") обробляє введення з клавіатури.
• Об'єкт cout (виголошується як "сі-аут" від англійського "see-out") обробляє вивід на екран.
• Об'єкт cerr (виголошується як "сі-ер" від англійського "see-err") обробляє виведення помилок, що не буферизує, на стандартний пристрій виведення повідомлень про помилки, тобто на екран. Оскільки вивід не буферизує, то всі дані, що направляються в сerr, відразу ж виводяться пристроєм виводу.
• Об'єкт clog (виголошується як "сі-балка" від англійського "see-log") обробляє повідомлення, що буферизують, про помилки, які виводяться на стандартний пристрій виведення повідомлень про помилки (екран). Частенько ці повідомлення переадресуються у файл реєстрації.
Переадресація
Кожен стандартний пристрій введення і виводу, у тому числі пристрій виведення повідомлень, про помилки може здійснювати переадресацію на інші пристрої. Наприклад, системні повідомлення про помилки часто переадресуються у файл реєстрації. Для введення і виведення даних програмою також можна використовувати файли, для чого служать спеціальні команди операційної системи.
Під переадресацією розуміють пересилку даних, що виводяться, в пристрій, або прочитування даних з пристрою, відмінне від встановленого за умовчанням.
Пайпінгом називається використання виведення однієї програми як введення для іншої.
В цілому переадресація більше відноситься до функцій операційної системи, а не бібліотек iostream. Мова C++ надає доступ до чотирьох стандартних пристроїв і необхідний набір команд для переадресації пристроїв введення-виводу.
Виведення даних за допомогою cin
Глобальний об'єкт cin відповідає за введення даних і стає доступним при включенні в програму класу iostream. У попередніх прикладах використовується переобтяжений оператор введення (>>) для привласнення даних, що вводяться, змінним програми. Для введення даних використовується наступний синтаксис".
int someVariable;
cout << "Enter а number: ";
cin >> someVariable;
Інший глобальний об'єкт, cout, і його використання для виведення даних обговорюється декілька нижче. Зараз же зупинимося на третьому рядку:
cin >> someVariable;.
Що ж є об'єктом cin?
На глобальність цього об'єкту вказує той факт, що його не потрібно оголошувати в коді програми. Об'єкт cin включає переобтяженого оператора введення (>>), яке записує дані, що зберігаються в буфері cin, в локальну змінну someVariable. Причому оператор введення переобтяжений таким чином, що личить для введення даних всіх базових типів, включаючи int&, short&, long&, double&, float&, char&, char* і тому подібне
Використання cin показане в лістингу 1.
Лістинг 1. Використання cin для введення даних різних типів
1: //Листинг 1. Введення даннах за допомогою cin
2:
3: #include <iostream.h>
4:
5: int main()
6: {
7: int myInt;
8: long myLong;
9: double myDouble;
10: float myFloat;
11: unsigned int myUnsigned;
12:
13: cout << "int: ";
14: cin >> myInt;
15: cout << "Long: ";
16: cin >> myLong;
17: cout << "Double: ";
18: cin >> myDouble;
19: cout << "Float: ";
20: cin >> myFloat;
21: cout << "Unsigned: ";
22: cin >> myUnsigned; 23:
24: cout << "\n\nInt:\t" << myInt << endl;
25: cout << "Long:\t" << myLong << endl;
26: cout << "Double:\t" << myDouble << endl;
27: cout << "Float:\t" << myFloat << endl;
28: cout << "Unsigned:\t" <<myUnsigned << endl;
29: return 0;
30: }
Результат:
int: 2
Long: 70000
Double: 987654321
Float: 3.33
Unsigned: 25
Int: 2
Long: 70000
Double: 9.87654e+08
Float: 3.33
Unsigned: 25
Аналіз: У рядках 7—11 оголошуються змінні різних типів. У рядках 13—22 користувачеві пропонується ввести значення для цих змінних, після чого результати виводяться в рядках 24—28 (за допомогою cin).
Інформація, що виводиться програмою, говорить про те, що змінні записуються і виводяться відповідно до їх типа.
Рядки
Об'єкт cin також може приймати як аргумент покажчик на рядок символів (char*), що дозволяє створювати буфер символів і заповнювати його за допомогою cin. Наприклад, можна написати наступне:
char YourName[50]
cout << "Enter your name: ";
cin >> YourName;
Якщо ввести ім'я Jesse, змінна YourName заповниться символами J, e, s, s, e і \0. Останнім буде кінцевий нульовий символ, оскільки cin автоматично вставляє його. Тому при визначенні розміру буфера потрібно поклопотатися про те, аби він був чималим і міг вміщати всі символи рядка, включаючи кінцевий нульовий символ.
Проблеми, що виникають при введенні рядків
Успішно виконавши всі описані раніше операції з об'єктом cin, ви будете неприємно здивовані, якщо спробуєте ввести в рядку повне ім'я. Річ у тому, що cin розглядає пропуск як заданий за умовчанням роздільник рядків. Після того, як в рядку виявляється пропуск, введення рядка завершується додаванням кінцевого нульового символу. Ця проблема показана в лістингу 2.
Лістинг 2. Спроба введення рядка з помощьм cin
1: //Лістинг 2. Проблеми з введенням рядка з допомогою cin
2:
3: #include <iostream.h>
4:
5: int main()
6: {
7: char YourName[50];
8: cout << "Your first name: ";
9: cin >> YourName;
10: cout << "Here it is: " << YourName << endl;
11: cout << "Your entire name: ";
12: cin >> YourName;
13: cout << "Here it is: " << YourName << endl;
14: return 0;
15: }
Результат:
Your first name: Jesse
Here it is: Jesse
Your entire name: Jesse Liberty
Here it is: Jesse
Аналіз: Рядком 7 для зберігання рядка, що вводиться користувачем, створюється масив символів. У рядку 8 користувачеві пропонується ввести ім'я, і, як видно з виводу, це ім'я зберігається правильно.
У рядку 11 користувачеві пропонується ввести не лише ім'я, але і прізвище. Введення здійснюється лише до тих пір, поки cin не виявить пропуск між ім'ям і прізвищем. Після цього введення рядка припиняється і інформація, що залишилася, втрачається. Це не зовсім те, що було потрібне.
Аби зрозуміти, чому cin працює саме так, проаналізуйте лістинг 3, в якому показаний приклад введення рядка значень.