Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичні вказівки до практичних робіт.docx
Скачиваний:
32
Добавлен:
07.06.2015
Размер:
3.36 Mб
Скачать

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

1. Опису структур для формування списків / дерев, а також функції для їх обробки зберегти в бібліотечному файлі з розширенням. H (наприклад, point.h). Функцію main () зберегти у файлі з розширенням. Cpp. Бібліотечний файл підключити за допомогою директиви # include "імя_файла.h".

2. Для виділення пам'яті під інформаційні поля типу char * використовувати операцію new, для видалення з пам'яті - операцію delete.

3. Для формування елементів списків / дерева написати окремі функції.

4. Для формування списків / дерева, видалення додавання елементів, пошуку заданих елементів написати окремі функції.

5. У функції main () повинні бути розміщені тільки опису змінних і звернення до відповідних функцій.

6. Якщо в списку / дереві відсутні елементи, відповідні критерію пошуку (наприклад, при видаленні елемента з номером k, k більше, ніж кількість елементів у списку), повинно бути виведено повідомлення про те, що необхідні елементи не знайдені.

7. Інтерфейс реалізувати за допомогою текстового меню.

6. Зміст звіту

1. Постановка завдання (загальна і для конкретного варіанту).

2. Визначення функцій для реалізації поставлених завдань.

3. Визначення функції main ().

4. Тести.

7. Контрольні питання

  1. Назвіть види динамічних структур даних

  2. Дайте визначення черги

  3. Дайте визначення списку

  4. Дайте визначення бінарному дереву

  5. Дайте визначення стеку

Практична робота № 20

Тема: Зберігання даних на зовнішніх носіях

1. Мета роботи:

1. Отримання практичних навичок запису структурованої інформації в файли в стилі С;

2. Отримання практичних навичок запису структурованої інформації в файли в стилі С + +;

2. Короткі теоретичні відомості

2.1. Потоковий ввод-вивід в стилі С

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

В С існують засоби введення-виведення. Всі операції введення-виведення реалізуються за допомогою функцій, які знаходяться в бібліотеці С. Бібліотека С підтримує три рівні введення-виведення:

• потоковий ввід-висновок;

• введення-виведення нижнього рівня;

• введення-виведення для консолі і портів (залежить від ОС).

Потік - це абстрактне поняття, що відноситься до будь переносу даних від джерела до приймача.

Читання даних з потоку називається витяганням, висновок у потік - приміщенням, або включенням.

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

Рисунок 25- Читання даних з потоку

При роботі з потоком можна:

• Відкривати і закривати потоки (пов'язувати покажчики на потік з конкретними файлами);

• вводити і виводити рядок, символ, відформатовані дані, порцію даних довільної довжини;

• аналізувати помилки вводу-виводу і досягнення кінця файлу;

• управляти буферизацією потоку і розміром буфера;

• отримувати і встановлювати покажчик поточної позиції у файлі.

Функції бібліотеки введення-виведення знаходяться в заголовному файлі <stdio.h>.

Перш ніж почати працювати з потоком, його треба ініціювати, тобто відкрити. При цьому потік пов'язується зі структурою зумовленого типу FILE, визначення якої знаходиться в бібліотечному файлі <stdio.h>. У структурі знаходиться покажчик на буфер, покажчик на поточну позицію файлу і т. п. При відкритті потоку, повертається покажчик на потік, тобто на об'єкт типу FILE.

# include <stdio.h>;

. . . . . . . .

FILE * fp;

. . . . . . . . . . ..

fp = fopen ("t.txt", "r");

де fopen (<ім'я файлу>, <режім_откритія>) - функція для ініціалізації файлу.

Існуючи режими для відкриття файлу представлені в таблиці

Таблиця 25 – Режими для відкриття файлу

Режим

Описание режима открытия файла

r

Файл открывается для чтения, если файл не существует , то выдается ошибка при исполнении программы.

w

Файл открывается для записи, если файл не существует, то он будет создан, если файл уже существует, то вся информация из него стирается.

a

Файл открывается для добавления, если фай не существует, то он будет создан, если существует, то информация из него не стирается, можно выполнять запись в конец файла

r+

Файл открывается для чтения и записи, изменить размер файла нельзя, если файл не существует , то выдается ошибка при исполнении программы.

w+

Файл открывается для чтения и записи, если файл не существует, то он будет создан, если файл уже существует, то вся информация из него стирается.

a+

Файл открывается для чтения и записи, если фай не существует, то он будет создан, если существует, то информация из него не стирается, можно выполнять запись в конец файла

Потік можна відкрити в текстовому (t) або довічним (b) режимі. За замовчуванням використовується текстовий режим. У явному вигляді режим вказується таким чином:

• "r + b" або "rb" - двійковий (бінарний) режим;

• "r + t" або "rt" - текстовий режим.

У файлі stdio.h визначена константа EOF, яка повідомляє про закінчення файлу (негативне ціле число).

При відкритті потоку можуть виникати такі помилки:

• файл, пов'язаний з потоком не знайдений (при читанні з файлу);

• диск заповнений (при записі);

• диск захищений від запису (при записі) і т. п.

У цих випадках покажчик на потік придбає значення NULL (0). Покажчик на потік, відмінний від аварійного не дорівнює 0.

Для виведення про помилку при відкритті потоку використовується стандартна бібліотечна функція з файлу <stdio.h>

void perror (const char * s);

if ((fp = fopen ("t.txt", "w") == NULL)

{

/ / Виводить рядок символів з повідомленням про помилку

perror ("\ nошібка при відкритті файлу");

exit (0);

}

Після роботи з файлом, його треба закрити

fclose (<указатель_на_поток>);

Коли програма починає виконуватися, автоматично відкриваються декілька потоків, з яких основними є:

• стандартний потік вводу (stdin);

• стандартний потік виводу (stdout);

• стандартний потік виводу про помилки (stderr).

За замовчуванням stdin ставиться у відповідність клавіатура, а потокам stdout і stderr - монітор. Для введення-виведення за допомогою стандартних потоків використовуються функції:

• getchar () / putchar () - введення-виведення окремого символу;

• gets () / puts () - ввід-вивід рядка;

• scanf () / printf () - форматований ввід / вивід.

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

Для символьного введення-виведення використовуються функції:

• int fgetc (FILE * fp), де fp - покажчик на потік, з якого виконується зчитування. Функція повертає черговий символ у формі int з потоку fp. Якщо символ не може бути прочитаний, то повертається значення EOF.

• int fputc (int c, FILE * fp), де fp - покажчик на потік, в який виконується запис, c - змінна типу int, в якій міститься записуваний в потік символ. Функція повертає записаний в потік fp символ у формі int. Якщо символ не може бути записаний, то повертається значення EOF.

Для рядковому введення-виведення використовуються наступні функції:

• char * fgets (char * s, int n, FILE * f), де char * s - адреса, за якою розміщуються лічені байти, int n - кількість лічених байтів, FILE * f - покажчик на файл, з якого виробляється зчитування.

Прийом байтів закінчується після передачі n-1 байтів або при отриманні керуючого символу '\ n'. Керуючий символ теж передається в приймаючу рядок. Рядок у будь-якому випадку закінчується '\ 0'. При успішному завершенні роботи функція повертає покажчик на прочитану рядок, при неуспішному - 0.

• int puts (char * s, FILE * f), де char * s - адресу, з якого беруться записувані в файл байти, FILE * f - покажчик на файл, в який проводиться запис.

Символ кінця рядка ('\ 0') в файл не записується. Функція повертає EOF, якщо при записі у файл сталася помилка, при успішній запису повертає невід'ємне число.

Для блокового вводу-виводу використовуються функції:

• int fread (void * ptr, int size, int n, FILE * f), де void * ptr - вказівник на область пам'яті, в якій розміщуються лічені з файлу дані, int size - розмір одного зчитуваного елемента, int n - кількість прочитуваних елементів, FILE * f - покажчик на файл, з якого виробляється зчитування.

У разі успішного зчитування функція повертає кількість лічених елементів, інакше - EOF.

• int fwrite (void * ptr, int size, int n, FILE * f), де void * ptr - вказівник на область пам'яті, в якій розміщуються лічені з файлу дані, int size - розмір одного записуваного елемента, int n - кількість записуваних елементів, FILE * f - покажчик на файл, в який проводиться запис.

У разі успішної запису функція повертає кількість записаних елементів, інакше - EOF.

У деяких випадках інформацію зручно записувати у файл без перетворення, тобто в символьному вигляді придатному для безпосереднього відображення на екран. Для цього можна використовувати функції форматованого введення-виведення:

• int fprintf (FILE * f, const char * fmt, ...), де FILE * f - покажчик на файл, в який проводиться запис, const char * fmt - форматна рядок,. . . - Список змінних, які записуються в файл.

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

• int fscanf (FILE * f, const char * fmt, par1, par2, ...), де FILE * f - покажчик на файл, з якого проводиться читання, const char * fmt - форматна рядок, par1, par2,. . . - Список змінних, в які заноситься інформація з файлу.

Функція повертає число змінних, яким присвоєно значення.

Засоби прямого доступу дають можливість переміщати покажчик поточної позиції в потоці на потрібний байт. Для цього використовується функція

• int fseek (FILE * f, long off, int org), де FILE * f - покажчик на файл, long off - позиція зсуву, int org - початок відліку.

Зсув задається вираз або змінної і може бути негативним, тобто можливе переміщення як у прямому, так і в зворотному напрямках. Початок відліку задається однією із визначених у файлі <stdio.h> констант:

SEEK_SET == 0 - початок файлу;

SEEK_CUR == 1 - поточна позиція;

SEEK_END == 2 - кінець файлу.

Функція повертає 0, якщо переміщення в потоці виконано успішно, інакше повертає ненульове значення.

2.2. Обробка елементів файлу

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

void del (char * filename)

{/ / Видалення запису з номером х

FILE * f ;/ / початковий файл

FILE * temp ;/ / допоміжний файл

/ / Відкрити вихідний файл для читання

f = fopen (filename, "rb");

/ / Відкрити допоміжний файл для запису

temp = fopen ("temp", "wb")

student a ;/ / буфер для читання даних з файлу

/ / Зчитуємо дані з вихідного файлу в буфер

for (long i = 0; fread (& a, sizeof (student), 1, f); i + +)

if (i! = x) / / якщо номер запису не дорівнює х

{

/ / Записуємо дані з буфера в тимчасовий файл

fwrite (& a, sizeof (student) 1, temp);

}

else

{

cout << a << "- is deleting ...";

}

fclose (f) ;/ / закриваємо вихідний файл

fclose (temp); / / закриваємо тимчасовий файл

remove (filename) ;/ / видаляємо вихідний файл

rename ("temp", filename) ;/ / перейменовуємо тимчасовий файл

}

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

Для додавання елементів в початок або в середину файлу також використовується допоміжний файл, в який в потрібне місце додаються нові дані.

Для додавання елементів кінець файлу достатньо відкрити його в режимі "a" або "a +" (для додавання) і записати нові дані в кінець файлу.

f = fopen (filename, "ab") ;/ / відкрити файл для додавання

cout << "\ nHow many records would you add to file?";

cin >> n;

for (int i = 0; i <n; i + +)

{

/ / Прочитати об'єкт

fwrite (& a, sizeof (student), 1, f) ;/ / записати в файл

}

fclose (f) ;/ / закрити файл

2.3. Потоковий ввод-вивід в стилі С + +

С + + надає можливість вводу / виводу як на низькому рівні - неформатований введення-виведення, так і на високому - форматований ввід-висновок. При неформатований вводі / виводі передача інформації здійснюється блоками байтів даних без будь-якого перетворення. При форматувати - байти групуються таким чином, щоб їх можна було сприймати як типізовані дані (цілі числа, рядки символів, числа з плаваючою комою і т. п.)

По напрямку обміну потоки можна розділити на

• вхідні (дані вводяться в пам'ять),

• вихідні (дані виводяться з пам'яті),

• двонаправлені (допускающие як витяг, так і включення).

По виду пристроїв, з якими працює потік, потоки можна розділити на стан-дротяні, файлові і рядкові:

• стандартні потоки призначені для передачі даних від клавіатури і на екран дисплея,

• файлові потоки - для обміну інформацією з файлами на зовнішних носіях даних (наприклад, на магнітному диску),

• рядкові потоки - для роботи з масивами символів в оперативній пам'яті.

Для роботи зі стандартними потоками бібліотека C + + містить бібліотеку <iostream.h>. При цьому в програмі автоматично стають доступними об'єкти:

• cin - об'єкт, відповідає стандартному потоку вводу,

• cout - об'єкт, відповідає стандартному потоку виводу.

Обидва ці потоку є буферізірованний.

Форматований ввід / вивід реалізується через дві операції:

• << - висновок в потік;

• >> - читання з потоку.

Використання файлів в програмі передбачає такі операції:

• створення потоку;

• відкриття потоку і зв'язування його з файлом;

• обмін (введення / виведення);

• знищення потоку;

• закриття файлу.

Для роботи зі файловими потоками бібліотека C + + містить бібліотеки:

• <ifstream.h> - для роботи з вхідними потоками,

• <ofstream.h> - для роботи з вихідними потоками

• <fstream.h> - для роботи з двонаправленими потоками.

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

Для створення файлового потоку використовуються спеціальні методи-конструктори, які створюють потік відповідного класу, відрозкривають файл з вказаним ім'ям і пов'язують файл з потоком:

• ifstream (const char * name, int mode = ios :: in) ;/ / вхідний потік

• ofstream (const char * name, int mode = ios :: out | ios :: trunc) ;/ / вихідний потік

• fstreamCconst char * name, int mode = ios :: in | ios :: out) ;/ / двонаправлений потік

Другим параметром є режим відкриття файлу. Замість значення за замовчуванням можна вказати одне з наступних значень, визначених в класі ios.

Таблиця 26 – Методи-конструктори

ios::in

открыть файл для чтения;

ios::out

открыть файл для записи;

ios::ate

установить указатель на конец файла, читать нельзя, можно только записывать данные в конец файла;

ios::app

открыть файл для добавления;

ios::trunc

если файл существует, то создать новый;

ios::binary

открыть в двоичном режиме;

ios::nocreate

если файл не существует, выдать ошибку, новый файл не открывать

ios::noreplace

если файл существует, выдать ошибку, существующий файл не открывать;

Відкрити файл у програмі можна з використанням або конструкторів, або методу open, має такі ж параметри, як і у відповідному конструкторі.

fstream f; / / створює файловий потік f

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

f.open (".. \ \ f.dat", ios :: in);

/ / Створює і відкриває для читання файловий потік f

fstream f (".. \ \ f.dat", ios :: in);

Після того як файловий потік відкритий, працювати з ним можна також як і зі стандартними потоками cin і cout. При читанні даних з вхідного файлу треба контролювати, чи був досягнутий кінець файлу після чергової операції виводу. Це можна робити за допомогою методу eof ().

Якщо в процесі роботи виникне помилкова ситуація, то потоковий об'єкт набуває значення рівне 0.

Коли програма залишає область видимості потокового об'єкта, то він знищується, при цьому перестає існувати зв'язок між потоковим об'єктом і фізичним файлом, а сам файл закривається. Якщо файл потрібно закрити раніше, то використовується метод close ().

/ / Створення файлу з елементів типу person

struct person

{

char name [20];

int age;

};

person * mas ;/ / динамічний масив

fstream f ("f.dat", ios :: out) ;/ / двонаправлений файловий потік

int n;

cout << "N?";

cin >> n;

mas = new person [n] ;/ / створюємо динамічний масив

for (int i = 0; i <n; i + +)

{

cout << "?";

/ / Введення одного елемента типу person зі стандартного потоку cin

cin >> mas [i]. name;

cin >> mas [i]. age;

}

/ / Запис елементів масиву в файловий потік

for (i = 0; i <n; i + +)

{

f << mas [i]. name; f << "\ n";

f << mas [i]. age; f << "\ n";

}

f.close () ;/ / закриття потоку

/ / Читання елементів з файлу

person p;

f.open ("f.dat", ios :: in) ;/ / відкриваємо потік для читання

do

{/ * Читаємо елементи типу person з файлового потоку f в змінну p * /

f >> p.name;f >> p.age;

/ / Якщо досягнуто кінець файлу, виходимо з циклу

if (f.eof ()) break;

/ / Вивід на екран

cout << p.name << "" << p.age << "\ n";

} While (! F.eof ());

f.close () ;/ / закриття потоку