
О.О.П / ооп / 4_кол / Лабораторні роботи / Лабораторна робота 9
.docОб’єктно-орієнтоване програмування
Лабораторна робота
Тема: Файли
Основн теоретичні відомості
Під введенням-виводом в програмуванні розуміється процес обміну інформацією між оперативною пам'яттю і зовнішніми пристроями: клавіатурою, дисплеєм, магнітними накопичувачами і тому подібне Введення — це занесення інформації із зовнішніх пристроїв в оперативну пам'ять, а вивід — винесення інформації з оперативної пам'яті на зовнішні пристрої. Такі пристрої, як дисплей і принтер, призначені лише для виводу; клавіатура — пристрій введення. Магнітні накопичувачі (диски, стрічки) використовуються як для введення, так і для виводу.
Основним поняттям, пов'язаним з інформацією на зовнішніх пристроях ЕОМ, є поняття файлу. Всяка операція введення-виводу трактується як операція обміну з файлами: введення — це читання з файлу в оперативну пам'ять; вивід — запис інформації з оперативної пам'яті у файл. Тому питання про організацію в мові програмування введення-виводу зводиться до питання про організацію роботи з файлами.
Внутрішній файл — це змінна файлового типа, що є структурованою величиною. Елементи файлової змінної можуть мати різного типа і, відповідно, різну довжину і форму внутрішньої вистави. Внутрішній файл зв'язується із зовнішнім (фізичним) файлом за допомогою стандартної процедури Assign. Один елемент файлової змінної стає окремим записом в зовнішньому файлі і може бути прочитаний або записаний за допомогою однієї команди. Спроба записати у файл або прочитати з нього величину, не співпадаючу за типом з типом елементів файлу, наводить до помилки.
Аналогом поняття внутрішнього файлу в мовах Си/Си++ є поняття потоку. Потік — це байтова послідовність, передавана в процесі введення-виводу.
Потік має бути пов'язаний з яким-небудь зовнішнім пристроєм або файлом на диску. У термінології Сі це звучить так: потік має бути направлений на якийсь пристрій або файл.
Основні відмінності файлів в Сі полягають в наступному: тут відсутнє поняття типа файлу і, отже, фіксованої структури запису файлу. Будь-який файл розглядається як байтова послідовність:
Стрілкою позначений покажчик файлу, що визначає поточний байт файлу. EOF є стандартною константою — ознакою кінця файлу.
Існують стандартні потоки і потоки, що оголошуються в програмі. Останні зазвичай зв'язуються з файлами на диску, що створюються програмістом. Стандартні потоки призначаються і відкриваються системою автоматично. З початком роботи будь-якої програми відкриваються 5 стандартних потоків, з яких основними є наступні:
• stdin — потік стандартного введення (зазвичай пов'язаний з клавіатурою);
• stdout — потік стандартного виводу (зазвичай пов'язаний з дисплеєм);
• stderr — виведення повідомлень про помилки (пов'язаний з дисплеєм).
Окрім цього, відкривається потік для стандартного друку і додатковий потік для послідовного порту.
Працюючи раніше з програмами на Сі, використовуючи функції введення з клавіатури і виводу на екран, ми вже неявно мали справу з першими двома потоками. А повідомлення про помилки, які система виводила на екран, відносилися до третього стандартного потоку. Потік для роботи з дисковим файлом має бути відкритий в програмі.
Робота з файлами на диску. Робота з дисковим файлом починається з оголошення покажчика на потік. Формат такого оголошення:
FILE *ім'я покажчика;
Наприклад:
FILE *fp;
Слово file є стандартним ім'ям структурного типа, оголошеного в заголовному файлі stdio.h. У структурі file міститься інформація, за допомогою якої ведеться робота з потоком, зокрема: покажчик на буфер, покажчик (індикатор) поточної позиції в потоці і так далі
Наступний крок — відкриття потоку, яке проводиться за допомогою стандартної функції fopen (). Ця функція повертає конкретне значення для покажчика на потік і тому її значення привласнюється оголошеному раніше покажчику. Відповідний оператор має формат:
ім'я_покажчика={open(ім'я файлу, режим_открытия);
Параметри функції fopen() є рядками, які можуть бути як константами, так і покажчиками на символьні масиви. Наприклад:
fp=fopen("test.dat","r") ;
Тут test. dat — це ім'я фізичного файлу в поточному каталозі диска, з яким тепер буде зв'язаний потік з покажчиком fp. Параметр режиму r означає, що файл відкритий для читання. Що стосується термінології, то допустимо вживати як вираз «відкриття потоку», так і вираження «відкриття файлу».
Існують наступні режими відкриття потоку і відповідні ним параметри:
Треба добре розуміти, що відкриття вже існуючого файлу для запису веде до втрати колишньої інформації в нім. Якщо такий файл ще не існував, то він створиться. Відкривати для читання можна лише існуючий файл.
Потік може бути відкритий або для текстового, або для двійкового (бінарного) режиму обміну.
Текстовий файл це послідовність символів, яка ділиться на рядки спеціальними кодами, — повернення каретки (код 13) і переклад рядка (код 10). Якщо файл відкритий в текстовому режимі, то при читанні з такого файлу комбінація символів «повернення каретки — переклад рядка» перетвориться в один символ \n — перехід до нового рядка.
При записі у файл здійснюється зворотне перетворення. При роботі з двійковим файлом жодних перетворень символів не відбувається, тобто інформація переноситься без всяких змін.
Вказані вище параметри режимів відкривають текстові файли. Якщо потрібно вказати на двійковий файл, то до параметра додається буква b. Наприклад: rb, або wb, або r+b. У деяких компіляторах текстовий режим обміну позначається буквою t, тобто записується a+t або rt.
Якщо при відкритті потоку з якої-небудь причини виникла помилка, то функція fopen() повертає значення константи NULL. Ця константа також визначена у файлі stdio.h. Помилка може виникнути через відсутність файлу, що відкривається, на диску, браки місця в динамічній пам'яті і тому подібне Тому бажано контролювати правильність проходження процедури відкриття файлу. Рекомендується наступний спосіб відкриття:
FILE *fp;
if (fp=fopen("test.dat","r")==NULL
{puts("He можу відкрити файл\n");
return;
}
В разі помилки програма завершить виконання із закриттям всіх раніше відкритих файлів.
Закриття потоку (файлу) здійснює функція fclose (), прототип якої має вигляд:
int fclose(FILE *fptr);
Тут fptr позначає формальне ім'я покажчика на потік, що закривається. Функція повертає нуль, якщо операція закриття прошла успішно. Інша величина означає помилку.
Запис і читання символів. Запис символів в потік проводиться функцією putс() з прототипом
int putc(int ch, FILE *fptr);
Якщо операція прошла успішно, то повертається записаний символ. В разі помилки повертається константа EOF.
Прочитування символу з потоку, відкритого для читання, проводиться функцією gets() з прототипом
int gets(FILE *fptr);
Функція повертає значення прочитуваного з файлу символу. Якщо досягнутий кінець файлу, то повертається значення EOF. Відмітимо, що це відбувається лише в результаті читання коди EOF.
Історично склалося так, що gets() повертає значення типа int. To же можна сказати і про аргумент ch в описі функції puts(). Використовується ж в обох випадках лише молодший байт. Тому обмін при зверненні може відбуватися і із змінними типа char.
Приклад 1. Складемо програму запису у файл символьної послідовності, що вводиться з клавіатури. Хай ознакою завершення введення буде символ *.
В результаті на диску (у каталозі, визначуваному системою) буде створений файл з ім'ям test.dat, який заповниться символами, що вводяться. Символ * у файл не запишеться.
Приклад 2. Файл, створений в результаті роботи попередньої програми, потрібно послідовно прочитати і вміст вивести на екран.
Зв'язок між результатом роботи попередньої програми і даною програмою здійснюється через ім'я фізичного файлу, яке в обох випадках має бути одним і тим же.
Запис і читання цілих чисел. Запис цілих чисел в потік без перетворення їх в символьну форму проводиться функцією putw () з прототипом
int putw(int, FILE *fptr) ;
Якщо операція прошла успішно, то повертається записане число. В разі помилки повертається константа EOF.
Прочитування цілого числа з потоку, відкритого для читання, проводиться функцією getw() з прототипом
int getw(FILE *fptr);
Функція
повертає значення прочитуваного з файлу
числа. Якщо прочитаний кінець файлу, то
повертається значення EOF.
Приклад 3. Складемо програму, по якій у файл запишеться послідовність цілих чисел, що вводяться з клавіатури, а потім ця послідовність буде прочитана і виведена на екран. Хай ознакою кінця введення буде число 9999.
Після завершення введення чисел у файл його необхідно закрити. При цьому відбувається скидання накопичених в буфері значень на диск. Лише після цього можна відкривати файл для читання. Покажчик встановлюється на початок потоку, і подальше читання відбуватиметься від початку до кінця файлу.
Запис і читання блоків даних. Спеціальні функції обміну з файлами є лише для символьного і цілого типів даних. У загальному випадку використовуються функції читання і запису блоків даних
З їх допомогою можна записувати у файл і читати з файлу дійсні числа, масиви, рядки, структури. При цьому, як і для раніше розглянутих функцій, зберігається форма внутрішнього представлення даних.
Функція запису блоку даних має прототип
int fread(void*buf, int bytes, int n, FILE*fptr);
Тут
buf — покажчик на адресу даних, які записуються у файл;
bytes — довжина в байтах однієї одиниці запису (блоку даних);
n — число блоків, передаваних у файл;
fptr — покажчик на потік.
Якщо запис виконався благополучно, то функція повертає число записаних блоків (значення n).
Функція читання блоку даних з файлу має прототип
int fwrite(void*buf, int bytes, int n, FILE*fptr);
По аналогії з попереднім описом легко зрозуміти сенс параметрів.
Приклад 4. Наступна програма організовує запис блоком у файл рядка (символьного масиву), а також читання і вивід на екран записаної інформації.
У цій програмі потік відкривається в режимі w+ (створення для запису з подальшим читанням). Тому закривати файл після запису не потрібно було. Новим елементом даної програми в порівнянні з попередніми є використання функції установки покажчика потоку в задану позицію. Її формат:
int fseek(покажчик на потік, зсув, начало_отсчета);
Початок відліку задається одній з констант, визначених у файлі stdio.h:
SEEK_SET (має значення 0) — початок файлу;
SEEK_CUR (має значення 1) — поточна позиція;
SEEK_END (має значення 2) — кінець файлу.
Зсув визначає число байт, на яке треба змістити покажчик відносно заданого початку відліку. Зсув може бути як позитивним, так і негативним числом. Обоє параметра мають типа long.
Обмін формату з файлами. За допомогою функції виводу формату можна формувати на диску текстовий файл з результатами обчислень, представленими в символьному вигляді. Надалі цей файл може бути проглянутий на екрані, роздрукований на принтері, відредагований за допомогою текстового редактора. Загальний вигляд функції виводу формату:
int fprintf(указатель_на_поток, форматная_строка, список змінних);
Що використалася нами раніше функція printf() для організації виводу на екран є приватним варіантом функції fprintf(). Функція printf() працює лише із стандартним потоком stdin, який завжди зв'язується системою з дисплеєм. Не буде помилкою, якщо в програмі замість printf() написати fprintf (stdin...).
Правила використання специфікаторів форматів при записі у файли на диску такі самі, як і при виводі на екран (див. разд. 4.4).
Приклад 5. Складемо програму, по якій буде розрахована і записана у файл таблиця квадратного коріння для цілих чисел від 1 до 10. Для контролю ця ж таблиця виводиться на екран.
Якщо тепер за допомогою текстового редактора (наприклад, вхідного в систему програмування) відкрити файл test.dat, то на екрані побачимо:
Тепер ці результати можна роздрукувати, включити в текст звіту і тому подібне
Введення формату з текстового файлу здійснюється за допомогою функції fscanf(), загальний формат якої виглядає таким чином:
int fscanf(покажчик на потік, форматная_строка, список адрес змінних);
Даною функцією зручно користуватися в тих випадках, коли вихідні дані заздалегідь готуються в текстовому файлі.
У наступному прикладі числові дані з файлу test.dat, отриманого в результаті виконання попередньої програми, вводяться в числові масиви Х і Y. Для контролю значення елементів масивів виводяться на екран. Заздалегідь за допомогою текстового редактора у файлі test.dat віддаляються два перші рядки із заголовками. В результаті у файлі залишаться лише числа.
Приклад 6.
Завдання
-
Дан файл, що містить текст російською мовою, і деякі букви. Знайти слово, що містить найбільшу кількість вказаних букв.
-
Дан файл, що містить текст російською мовою, і деяка буква. Підрахувати, скільки слів починається з вказаної букви.
-
Дан файл, що містить текст російською мовою. Знайти слово, що зустрічається в кожному реченны, або повідомити, що такого слова немає.
-
Дан файл, що містить текст, що включає російські і англійські слова. Підрахувати, які букв в тексті більше — російських або латинських.
-
Дан файл, що містить текст. Скільки слів в тексті? Скільки цифр в тексті?
-
Дан файл, що містить текст, що включає російські і англійські слова. Отримати новий файл, замінивши в початковому всі заголовні букви рядковими і навпаки.
* Дан файл, що містить зашифрований російський текст. Кожна буква замінюється на наступну за нею (буква я замінюється на а). Отримати в новому файлі розшифровку даного тексту.
Контрольні питання
1. Скласти програму, яка формує файл цілих чисел, що отримуються за допомогою датчика випадкових чисел.
2. Скласти програму, яка у файлі, сформованому програмою з попереднього завдання, знаходить найбільше і найменше значення.
3. Скласти програму, яка формує файл з рядкових латинських букв, вибираних випадковим чином.
4. Скласти програму, яка у файлі, сформованому програмою з попереднього завдання, підраховує кількість букв z.