Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP_LAB_Part_2_Obyektno-orientov_progr.DOC
Скачиваний:
11
Добавлен:
03.05.2019
Размер:
418.3 Кб
Скачать

Лабораторна робота №7. Потокові класи.

Мета. Навчитися програмувати ввід і вивід у С++, використовуючи об'єкти потокових класів стандартної бібліотеки С++.

Основний зміст роботи.

Створення даних типу користувача, створення і збереження об'єктів цього типу у файлі, читання їх з файлу, видалення з файлу, коректування у файлі, створення маніпуляторів користувача.

Основні теоретичні відомості.

Поняття потоку.

Потокові класи представляють об'єктно-орієнтований варіант функцій ANSI-C. Потік даних між джерелом і приймачем при цьому має наступні властивості:

  • Джерело чи приймач даних визначається об'єктом потокового класу.

  • Потоки використовуються для вводу-виводу високого рівня.

  • Загальноприйняті стандартні С-функції вводу/виводу розроблені як функції потокових класів, щоб полегшити перехід від С-функцій до С++ класів.

  • Потокові класи поділяються на три групи (шаблонів класів)

  • basic_istream, basic_ostream – загальні потокові класи, що можуть бути зв'язані з будь-яким буферним об'єктом;

  • basic_ifstream, basic_iostream – потокові класи для зчитування і запису файлів;

  • basic_istringstream, basic_ostringstream – потокові класи для об'єктів-рядків.

  • Кожен потоковий клас підтримує буферний об'єкт, що надає пам'ять для переданих даних, а також найважливіші функції вводу/виводу низького рівня для їх обробки.

  • Базовим шаблоном класів basic_ios для (потокових класів) і basic_streambuf (для буферних класів) передаються по два параметра шаблона:

  • перший параметр (char) визначає символьний тип;

  • другий параметр (traits) – об'єкт типу ios_traits (шаблон класу), у якому заданий тип і функції специфічні для використаного символьного типу;

  • для типів char і wchar_t утворені відповідні об'єкти типу ios_traits і потокові класи.

Приклад шаблона потокового класу.

template <class char, class traits = ios_traits <char>> class basic_istream: virtual public basic_ios <char, traits>;

Потокові класи в С++.

Бібліотека потокових класів С++ побудована на основі двох базових класів: ios і streambuf .

Клас streambuf забезпечує організацію і взаємозв'язок буферів вводу-виводу, розташовуваних у пам'яті, з фізичними пристроями вводу-виводу. Методи і дані класу streambuf програміст явно звичайно не використовує. Цей клас потрібний іншим класам бібліотеки вводу-виводу. Він доступний і програмісту для створення нових класів на основі вже існуючих.

Схема ієрархії

Клас ios містить засоби для форматованого вводу-виводу і перевірки помилок.

Схема ієрархії

istream – клас вхідних потоків;

ostream – клас вихідних потоків;

iostream – клас вводу-виводу;

istrstream – клас вхідних строкових потоків;

ifstream – клас вхідних файлових потоків і т.д.

Потокові класи, їх методи і дані стають доступними в програмі, якщо в неї включений потрібний заголовний файл.

iostream.h - для ios, ostream, istream.

strstream.h - для strstream, istrstream, ostrstream

fstream.h - для fstream, ifstream, ofstream

Базові потоки вводу-виводу

Для введення з потоку використовуються об'єкти класу istream. Для виводу в потік - об'єкти класу ostream.

У класі istream визначені наступні функції.

  • istream& get (char* buffer, int size, char delimiter='\n');

Ця функція витягає символи з istream і копіює їх у буфер. Операція припиняється при досягненні кінця файлу, або коли буде скопійовано size символів, або при виявленні зазначеного роздільника. Сам роздільник не копіюється і залишається в streambuf. Послідовність прочитаних символів завжди завершується нульовим символом.

  • istream& read(char* buffer,int size);

Не підтримує роздільників, і зчитані в буфер символи не завершуються нульовим символом. Кількість зчитаних символів запам'ятовується в istream::gcount_ (private).

  • istream& getline(char* buffer,int size, char delimiter='\n');

Роздільник витягається з потоку, але в буфер не заноситься. Це основна функція для витягу рядків з потоку. Зчитані символи завершуються нульовим символом.

  • istream& get(streambuf& s,char delimiter='\n');

Копіює дані з istream у streambuf доти, поки не знайде кінець файлу чи символ-роздільник. Останній не витягається з istream. У s нульовий символ не записується.

  • istream get (char& C);

Читає символ з istream у С. У випадку помилки, С приймає значення 0XFF.

  • int get();

Витягає з istream черговий символ. При виявленні кінця файлу повертає EOF.

  • int peek();

Повертає черговий символ з istream, не витягаючи його з istream.

  • int gcount();

Повертає кількість символів, зчитаних під час останньої операції неформатованого вводу.

  • istream& putback(З)

Якщо в області get об'єкта streambuf є вільний простір, то туди міститься символ С.

  • istream& ignore(int count=1,int target=EOF);

Витягає символ з istream, поки не відбудеться наступне:

  • функція не витягне count символів;

  • не буде виявлений символ target;

  • не буде досягнуто кінця файлу.

У класі ostream визначені наступні функції.

  • ostream& put(char C);

Поміщає в ostream символ С.

  • ostream& write(const char* buffer,int size);

Записує в ostream вміст буфера. Символи копіюються доти, поки не виникне помилка чи не буде скопійовано size символів. Буфер записується без форматування. Обробка нульових символів нічим не відрізняється від обробки інших. Дана функція здійснює передачу неопрацьованих даних (бінарних чи текстових) у ostream.

  • ostream& flush();

Скидає буфер streambuf.

Для прямого доступу використовуються наступні функції установки позиції читання - запису.

При читанні

  • istream& seekg(long p);

Встановлює покажчик потоку get (не плутати з функцією) зі зсувом р від початку потоку.

  • istream& seekg(long p,seek_dir point);

Вказується початкова точка переміщення

  • enum seek_dir{beg,curr,end}

Позитивне значення р переміщає покажчик get вперед (до кінця потоку), негативне значення р – назад (до початку потоку).

  • long tellg();

Повертає поточне положення покажчика get.

При записі

  • ostream& seekp(long p);

Переміщає покажчик put у streambuf на позицію р від початку буфера streambuf.

  • ostream& seekp(long p,seek_dir point);

Вказується точка відліку.

  • long tellp();

Повертає поточне положення покажчика put.

Крім цих функцій у класі istream перевантажена операція >>, а в класі ostream <<. Операції << і >> мають два операнда Лівим операндом є об'єкт класу istream (ostream), а правим – дане, тип якого заданий у мові.

Для того щоб використовувати операції << і >> для всіх стандартних типів даних використовується відповідне число перевантажених функцій operator<< і operator>>. При виконанні операцій вводу-виводу в залежності від типу правого операнда викликається та чи інша перевантажена функція operator.

Підтримуються наступні типи даних: цілого, речовинні, рядка (char*). Для висновку – void* (усі покажчики, відмінні від char*, автоматично переводяться до void*). Перевантаження операції >> і << не змінюють їх пріоритету.

Функції operator<< і operator>>повертають посилання на той потоковий об'єкт, що зазначений ліворуч від знака операції. Таким чином, можна формувати “ланцюжок ” операцій.

cout << a << b << c;

cin >> i >> j >> k;

При вводі-виводі можна виконувати форматування даних.

Щоб використовувати операції >> і << з даними типів користувачів, обумовлених користувачем, необхідно розширити дія цих операцій, ввівши нові операції-функції. Першим параметром операції-функції повинна бути посилання на об'єкт потокового типу, другим – посилання на об'єкт типу користувача.

У файлі iostream.h визначені наступні об'єкти, зв'язані зі стандартними потоками вводу-виводу.

cin – об'єкт класу istream, зв'язаний зі стандартним буферизованим вхідним потоком.

cout – об'єкт класу ostream, зв'язаний зі стандартним буферизованим вихідним потоком.

cerr – не буферизований вихідний потік для повідомлення про помилки.

clog – буферизований вихідний потік для повідомлення про помилки.

Форматування

Безпосереднє застосування операцій вводу << і виводу >> до стандартних потоків cout, cin, cerr, clog для даних базових типів приводить до використання форматів зовнішнього представлення, що “замовчуються”, значень, що пересилаються.

Формати представлення виведеної інформації і правила сприйняття даних при вводі можуть бути змінені програмістом за допомогою прапорів форматування. Ці прапори успадковані всіма потоками з базового класу ios. Прапори форматування реалізовані у виді окремих фіксованих бітів і зберігаються в protected компоненті класу long x_flags. Для доступу до них наявні відповідні public функції.

Крім прапорів форматування використовуються наступні protected компонентні дані класу ios.

int x_width – мінімальна ширина поля виводу.

int x_precision – точність представлення дійсних чисел (кількість цифр дробової частини) при виводі.

int x_fill – символ заповнювач при виводі, за замовчуванням – пробіл.

Для одержання (встановлення) значень цих полів використовуються наступні компонентні функції

int width();

int width(int);

int precision();

int precision(int);

char fill();

char fill(char);

Маніпулятори

Незважаючи на гнучкість і великі можливості керування форматами за допомогою компонентних функцій класу ios, їхнє застосування достатньо громіздке. Більш простий спосіб зміни параметрів і прапорів форматування забезпечують маніпулятори.

Маніпуляторами називаються спеціальні функції, що дозволяють модифікувати роботу потоку. Особливість маніпуляторів полягає в тому, що їх можна використовувати в якості правого операнда операції >> чи <<. У якості лівого операнда, як звичайно, використовується потік (посилання на потік), і саме на цей потік впливає маніпулятор.

Для забезпечення роботи з маніпуляторами в класах istream і ostream є наступні перевантажені функції operator.

istream& operator>>(istream&(*_f)( istream&));

ostream& operator<<(ostream&(*_f)(ostream&));

При використанні маніпуляторів варто включити заголовний файл <iomanip.h>, у якому визначені вбудовані маніпулятори.

Визначення користувальницьких маніпуляторів

Порядок створення маніпулятора користувача з параметрами, наприклад для виводу наступний:

1.Визначити клас (my_manip) з полями: параметри маніпулятора + поле – покажчик на функцію типу

ostream& (*f)(ostream&,<параметри маніпулятора>);

2.Визначити конструктор цього класу (my_manip) з ініціалізацією полів.

3.Визначити в цьому класі дружню функцію – operator<<. Ця функція як правий аргумент приймає об'єкт класу (my_manip) лівого аргументу (операнда), потік ostream і повертає потік ostream як результат виконання функції *f. Наприклад,

typedef far ostream&(far *PTF)(ostream&,int,int,char);

class my_man{

int w;int n;char fill;

PTF f;

public:

//конструктор

my_man(PTF F,int W,int N,char FILL):f(F),w(W),n(N),fill(FILL){}

friend ostream& operator<<(ostream&,my_man& );

};

ostream& operator<<(ostream& out,my_man& my)

{return my.f(out,my.w,my.n,my.fill);}

4.Визначити функцію типу *f (fmanip), що приймає потік і параметри маніпулятора і повертаючу потік. Ця функція власне і виконує форматування. Наприклад,

ostream& fmanip(ostream& s,int w,int n,char fill)

{s.width(w);

s.flags(ios::fixed);

s.precision(n);

s.fill(fill);

return s;}

5.Визначити власне маніпулятор (wp) як функцію, що приймає параметри маніпулятора і повертає об'єкт my_manip, поле f якого містить покажчик на функцію fmanip. Наприклад,

my_man wp(int w,int n,char fill)

{return my_man(fmanip,w,n,fill);}

Для створення користувальницьких маніпуляторів з параметрами можна використовувати макроси, що містяться у файлі <iomanip.h>:

OMANIP(int)

IMANIP(int)

IOMANIP(int)

Стан потоку

Кожен потік має зв'язаний з ним стан. Стани потоку описуються в класі ios у виді перерахування enum.

public:

enum io_state{

goodbit, //немає помилки 0Х00

eofbit, //кінець файлу 0Х01

failbit, //остання операція не виконалася 0Х02

badbit, //спроба використання неприпустимої операції 0Х04

hardfail //фатальна помилка 0Х08

};

Прапори, що визначають результат останньої операції з об'єктом ios, містяться в змінній state. Одержати значення цієї змінної можна за допомогою функції int rdstate().

Крім того, перевірити стан потоку можна наступними функціями:

int bad(); 1, якщо badbit чи hardfail

int eof(); 1, якщо eofbit

int fail(); 1, якщо failbit, badbit чи hardfail

int good(); 1, якщо goodbit

Якщо операція >> використовується для нових типів даних, то при її перевантаженні необхідно передбачити відповідні перевірки.

Файловий ввід-вивід

Потоки для роботи з файлами створюються як об'єкти наступних класів:

ofstream – запис у файл;

ifstream – читання з файлу;

fstream – читання/запис.

Для створення потоків маються наступні конструктори.

  • fstream();

створює потік, не приєднуючи його ні до якого файлу.

  • fstream(const char* name,int mode,int p=filebuf::openprot);

створює потік, приєднує його до файлу з ім'ям name, попередньо відкривши файл, встановлює для нього режим mode і рівень захисту p. Якщо файл не існує, то він створюється. Для mode=ios::out, якщо файл існує, те його розмір буде усічений до нуля.

Прапори режиму визначені в класі ios і мають наступні значення.

in -для читання

out -для запису

ate -індекс потоку поміщений у кінець файлу. Читання більше не припустиме, виведені дані записуються в кінець файлу.

app -потік відкритий для додавання даних у кінець. Незалежно від seekp() дані будуть записуватися в кінець

trunc -усікання існуючого потоку до нуля

nocreate -команда відкриття потоку буде завершена невдало, якщо файл не існує

noreplace -команда відкриття потоку буде завершена невдало, якщо файл існує

binary - потік відкривається для двійкового обміну

Якщо при створенні потоку він не приєднаний до файлу, то приєднати існуючий потік до файлу можна функцією

void open(const char* name,int mode,int p=filebuf::openprot);

Функція

void fstreambase::close();

скидає буфер потоку, від'єднує потік від файлу і закриває файл. Цю функцію необхідно явно викликати при зміні режиму роботи з потоком. Автоматично вона викликається тільки при завершенні програми.

У такий спосіб створити потік і зв'язати його з файлом можна трьома способами:

1.

Створюється об'єкт filebuf

filebuf fbuf;

Об'єкт filebuf зв'язується з пристроєм (файлом)

fbuf.open(“ім'я”,ios::in);

Створюється потік і зв'язується з filebuf

istream stream(&fbuf);

2.

Створюється об'єкт fstream (ifstream, ofstream)

fstream stream;

Відкривається файл, що зв'язується через filebuf з потоком

stream.open(“ім'я”,ios::in);

3.

Створюється об'єкт fstream, одночасно відкривається файл, що зв'язується з потоком

fstream stream(“ім'я”,ios::in);

Порядок виконання роботи.

1.Визначити тип даних (клас) користувача. Визначити і реалізувати в ньому конструктори, деструктор, операції присвоювання, вводу і виводу для стандартних потоків.

2.Написати програму №1 для створення об'єктів класу користувача (вводу вихідної інформації з клавіатури з використанням перевантаженої операції “>>”) і збереження їх у потоці (файлі). Передбачити в програмі вивід повідомлення про кількість збережених об'єктів і про довжину отриманого файлу в байтах.

3.Виконати тестування програми.

4.Реалізувати для висновку в потік свій маніпулятор з параметрами.

5.Написати програму №2 для читання об'єктів з потоку, збереження їх у масиві і перегляду масиву. Для перегляду об'єктів використовувати перевантажену для cout операцію “<<” і свій маніпулятор. Передбачити в програмі вивід повідомлення про кількість прочитаних об'єктів і байтів.

6.Виконати програму для читання з файлу збережених попередньої програмою об'єктів і їх перегляду.

7.Написати програму №3 для додавання об'єктів у потік.

8.Виконати програму, додавши в потік кілька об'єктів і переглянути отриманий файл.

9.Написати програму №4 для видалення об'єктів з файлу.

10.Виконати програму, видаливши з потоку кілька об'єктів і переглянути отриманий файл.

11.Написати програму №5 для коректування (тобто заміни) записів у файлі.

12.Виконати програму і переглянути отриманий файл.

Методичні вказівки.

1.Програми створюється як EasyWin-додаток у Borland C++5.02.

Проект повинний містити 5 цільових вузлів (по числу програм).

2.Як користувальницький тип даних узяти клас з лабораторної роботи №1. Поля класу типу char* замінити на char[ціле].

3.У сукупності програми повинні використовувати всі класи потоків: istream, ostream, fstream, ifstream, ofstream.

4.Також у програмах варто показати всі три способи створення потоку і відкриття файлу (див. вище).

5.Необхідно продемонструвати читання з файлу і запис у файл як за допомогою функцій read/write, так і за допомогою перевантажених операцій >> і <<.

6.Маніпулятор користувача створюється з не менш, ніж із двома параметрами.

7.Визначення класу користувача зберегти в h-файлі.

8.Визначення компонентних функцій класу користувача зберегти в cpp-файлі.

9.Реалізацію маніпулятора зберегти в h-файлі.

Як параметри маніпулятора можна використовувати:

а) ширину поля виводу;

б) точність виводу дійсних чисел:

в) символ - заповнювач:

г) спосіб вирівнювання (до лівої чи правої межі). і т.д.

10.У потік записати не менш 5-ти об'єктів.

11.Після запису об'єктів у файл і перед читанням їх з файлу визначити кількість записаних об'єктів і вивести цю інформацію.

Визначити кількість записаних у файл об'єктів можна в такий спосіб:

а) стати на кінець файлу - функції seekp(),seekg().

б) визначити розмір файлу в байтах - функції tellp(),tellg().

в) визначити кількість записаних об'єктів - розмір файлу поділити на розмір об'єкта.

12.Необхідно тестувати помилки при роботі з файлом. Для цього варто використовувати перевантажені операції operator!(), operator void*() і функції bad(), good().

13.Оскільки у файлі може зберігатися кожна, заздалегідь не відома, кількість об'єктів, для їх збереження в програмі №2 при читанні з файлу використовувати динамічний масив.

14.Варто визначити функцію find(), що приймає значення ключового полю об'єкта і повертає зсув цього об'єкта від початку файлу. Викликати цю функцію перед видаленням/зміною об'єкта у файлі.

15.Для зміни і видалення об'єкта написати функції del() і repl(), яким передається посилання на потік, зсув від початку файлу змінної чи запису, що видаляється, (результат виклику функції find), нове значення змінюваного запису.

Зміст звіту.

1.Титульний лист.

2.Постановка задачі.

3.Визначення класу користувача.

4.Реалізація маніпулятора.

5.Реалізація функцій find(), del() і repl().

6.Пояснення до програм. Для кожної програми вказуються які потокові класи в ній використовуються, як створюються об'єкти потокових класів, як відкриваються файли, яким чином виконується ввід і вивід даних

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]