Лекція № 8 Тема: Файли. Робота з файлами. План Загальні поняття про потік Відкриття та закриття потоку Функції для роботи з файлами Двійковий режим обміну з файлами Загальні поняття про потік На рівні потокового вводу/виводу обмін даними виконується побайтно. Для файлів на диску за одне звертання до пристрою виконується читання або запис фіксованої порції даних. Частіше всього мінімальною порцією даних, що бере участь в обміні з зовнішньою пам'яттю, є блоки в 512 байт або 1024 байти. При введенні з диска (при читанні з файлу) дані поміщаються в буфер операційної системи, а потім побайтно або визначеними порціями передаються програмі користувача. При виведенні даних у файл вони нагромаджуються в буфері, а при заповненні буфера записуються у вигляді єдиного блоку на диск за одне звернення до нього. Буфери операційної системи реалізуються у вигляді ділянок основної пам'яті. Тому пересилання між буферами введення-виведення виконуваною програмою відбуваються достатньо швидко на відміну від реальних обмінів з фізичними пристроями. Таким чином, потік - це файл разом із засобами буферизації, які надаються. При роботі з потоком можна проводити наступні дії: • відкривати і закривати потоки (зв'язувати покажчики на потоки з конкретними файлами); • вводити і виводити: символ, рядок, форматовані дані, порцією даних довільної довжини; • аналізувати помилки потокового введення-виведення і умову досягнення кінця потоку (кінця файлу); • управляти буферизацією потоку і розміром буфера; • одержувати і встановлювати покажчик (індикатор) поточної позиції в потоці. Для того, щоб можна було використовувати функції бібліотеки введення-виведення мови Сі, в програму необхідно включити файл stdio.h (#include <stdio.h>), який містить прототипи функцій введення-виведення, а також визначення констант, типів і структур, необхідних для роботи функцій обміну з потоком. Відкриття і закриття потоку Перш ніж почати працювати з потоком, його необхідно ініціалізувати, тобто відкрити. При цьому потік зв'язується в виконуваній програмі із структурою наперед визначеного типу FILE. Визначення структурного типу FILE знаходиться в файлі stdio.h. В структурі FILE містяться компоненти, за допомогою яких ведеться робота з потоком, зокрема: покажчик на буфер, покажчик (індикатор) поточної позиції в потоці і інша інформація. При відкритті потоку в програму повертається покажчик на потік, який є покажчиком на об'єкт структурного типу FILE. Цей покажчик ідентифікує потік в усіх наступних операціях. Покажчик на потік, наприклад fp, повинен бути оголошений в програмі таким чином: #include <stdio.h> FILE *fp; Покажчик на потік набуває значення в результаті виконання функції відкриття потоку: fp = fopen (ім'я_файлу, режим_відкриття); Параметри ім'я_файлу і режим_відкриття є покажчиками на масиви символів, які містять відповідно ім'я файлу, пов'язаного з потоком, і рядок режимів відкриття. Однак ці параметри можуть задаватися і безпосередньо у вигляді рядків при виклику функції відкриття файлу: fp = fopen("t.txt", "r"); де t. txt - ім'я деякого файлу, пов'язаного з потоком; r - позначення одного з режимів роботи з файлом (тип доступу до потоку). Стандартно файл, пов'язаний з потоком, можна відкрити в одному з наступних шести режимів: "w" - новий текстовий файл відкривається для запису. Якщо файл вже існував, то попередній вміст стирається, файл створюється наново; "r" - існуючий текстовий файл відкривається тільки для читання; "а" -текстовий файл відкривається (або створюється, якщо файлу немає) для додавання в нього нової порції інформації (додавання в кінець файлу). На відміну від режиму "w" режим "а" дозволяє відкривати вже існуючий файл, не знищуючи його попередньої версії, і писати в продовження файлу; "w+" - новий текстовий файл відкривається для запису і подальших багатократних виправлень. Якщо файл вже існує, то попередній вміст стирається. Подальші після відкриття файлу запис і читання з нього допустимі в будь-якому місці файлу, у тому числі запис дозволений і в кінець файла, тобто файл може збільшуватися; "r+" - існуючий текстовий файл відкривається як для читання, так і для запису в будь-якому місці файлу; однак в цьому режимі неможливий запис в кінець файлу, тобто неприпустимо збільшення розмірів файла; "а+" -текстовий файл відкривається або створюється (якщо файлу немає) і стає доступним для змін, тобто для запису і для читання в будь-якому місці; при цьому на відміну від режиму "w+" можна відкрити існуючий файл і не знищувати його вмістимого; на відміну від режиму "г+" в режимі "а+" можна вести запис в кінець файлу, тобто збільшувати його розміри. Потік можна відкрити в текстовому або двійковому (бінарному) режимі. В текстовому режимі прочитана з потоку комбінація символів CR (значення 13) і LF (значення 10), тобто управляючі коди "повернення каретки" і "перевід рядка", перетворюються в один символ нового рядка '\n' (значення 10, співпадаюче з LF). При записі в потік в текстовому режимі здійснюється зворотне перетворення, тобто символ нового рядка '\n' (LF) замінюється послідовністю CR і LF. Якщо файл, пов'язаний з потоком, зберігає не текстову, а довільну двійкову інформацію, то вказані перетворення не потрібні і можуть бути навіть шкідливими. Обмін без такого перетворення виконується при виборі двійкового або бінарного режиму який позначається буквою b. Наприклад, "r+b" або "wb". В деяких компіляторах текстовий режим обміну позначається буквою t, тобто записують "a+t" або "rt". Якщо потік відкритий для змін, тобто в параметрі режиму присутній символ "+", то дозволено як введення в потік, так і читання з нього. Проте зміна режиму (перехід від запису до читання і назад) повинна відбуватися тільки після установки покажчика потоку в потрібну позицію. Відкриті на диску файли після закінчення роботи з ними рекомендується закрити явно. Для цього використовується бібліотечна функція int fclose (покажчик_на_поток); Відкритий файл можна відкрити повторно (наприклад, для змінення режиму роботи з ним) тільки після того, як файл буде закритий за допомогою функції fclose(). Функції для роботи з файлами В мові Сі можливо організовувати роботу з файлами на диску. Для цієї мети в бібліотеці мови Сі включені наступні функції: fgetc(), getc() – ввід (читання) одного символу з файлу; fputs(), putc() – запис одного символу в файл; fprintf() – форматований вивід в файл; fscanf() – фор матований ввід (читання) з файлу; fgets( ) - введення (читання) рядка з файлу; fputs( ) - запис рядка у файл. Двійковий (бінарний) режим обміну з файлами. Двійковий режим обміну організовується за допомогою функцій getc() і putc(), звернення до яких має наступний формат: с= getc(fp); putc(c, fp); де fp - покажчик на потік; c — змінна типу int для прийому чергового символу з файлу або для запису її значення у файл. Прототипи функції: int getc ( FILE *stream); int putc (int с, FILE * stream); Як приклад використання функцій getc( ) і putc() розглянемо програми введення даних у файл з клавіатури і програму виведення їх на екран дисплея з файлу. Програма введення читає символи з клавіатури і записує їх у файл. Ознакою завершення введення служить поступивший від клавіатури символ '#'. Ім'я файлу запрошується у користувача. Якщо при введенні послідовності символів натискається клавіша <Enter>, яка служить роздільником рядків при введенні з клавіатури, то у файл записуються управляючі коди "Повернення каретки" (CR - значення 13) і "Переклад рядка" (LF — значення 10). Код CR при виведенні викликає перевід маркера (курсора) в початок рядка екрану дисплея. Код LF служить для переводу маркера на новий рядок дисплея. Значення цих кодів в тексті програми позначені відповідно ідентифікаторами CR і LF, тобто CR і LF - іменовані константи. Запис управляючих кодів CR і LF у файл дозволяє при подальшому виведенні файлу на екран відділити рядки один від одного.
/* Програма введення символів */ #include <stdio.h> int main () FILE *fp; /* Покажчик на потік */ char с; /* Восьмирічний код "Повернення каретки": */ const char CR='\015'; /* Восьмерічний код "Переклад рядка": */ const char LF = '\012'/ char fname[20]; /* Масив для імені файлу */ /* Запит імені файлу: */ puts("введіть ім'я файлу: \n"); gets(fname) /* Відкрити файл для запису: */ if ((fp = fopen(fname,"w")) == NULL) { реrror(fname); return 1; } /* Цикл введення і запису у файл символів: */ while ((c = getchar()) != '#') { if (c == '\n') { putc(CR, fp); putc(LF, fp); } else putc(с, fp); } /* Цикл введення завершений; закрити файл: */ fclose(fp); return 0; } Наступна програма читає потік символів з раніше створеного файлу і виводить його на екран дисплея: /* Програма виведення символьного файлу на екран дисплея */ #include <stdio.h> int main () { FILE *fp; /* Покажчик на потік */ char с; char fname[20]; /* Масив для імені файлу */ /* Запит імені файлу: */ puts("введіть ім'я файлу: \n ") gets(fname); /* Відкрити файл для читання: */ if ((fp = fopen(fname,"r")) = = NULL) { реrror(fname); return 1; } /* Цикл читання з файлу і висновку символів на екран: */ while ((с = getc(fp)) != EOF) putchar(с); fclose(fp); /* Закрити файл */ return 0 }