Попередня сторінка Зміст Наступна сторінка Електронні посібники ВНТУ
ЛАБОРАТОРНА РОБОТА № 8
РОБОТА З ФАЙЛАМИ

Мета роботи
- Вивчити програмні засоби для роботи з файлами та потоками.
 - Дослідити основні функції роботи з файлами та реалізовувати найпростіші операції з ними.
 - Навчитись застосовувати у своїх програмах вхідні і вихідні текстові і бінарні файли і файлові потоки.
 

ТЕОРЕТИЧНІ ВІДОМОСТІ
Поняття файлів і потоків
В комп’ютерній системі між програмою і пристроєм знаходиться щось більш загальне, ніж сам пристрій. Такий узагальнений пристрій введення або виведення (пристрій більш високого рівня абстракції) називається потоком, в той час як конкретний пристрій називається файлом (файл – теж поняття абстрактне). Потоки бувають двох видів: текстові і двійкові.
Текстовий потік – це послідовність символів. У стандарті С вражається, що текстовий потік організований у вигляді рядків, кожен з яких закінчується символом нового рядка. Проте, в кінці останнього рядка цей символ не є обов'язковим. В текстовому потоці на вимогу базового середовища можуть відбуватися певні перетворення символів. Тому може і не бути однозначної відповідності між символами, які пишуться (читаються), і тими, які зберігаються в зовнішньому пристрої. Крім того, кількість тих символів, які пишуться (читаються), і тих, які зберігаються в зовніньому пристрої, може також не співпадати із-за можливих перетворень.
Двійковий потік – це послідовність байтів, яка взаємно однозначно відповідає байтам на зовнішньому пристрої, причому ніякого перетворення символів не відбувається. Крім того, кількість тих байтів, які пишуться (читаються), і тих, які зберігаються на зовнішньому пристрої, однакова. Однак наприкінці двійкового потоку може додаватися визначена додатком кількість нульових байтів (наприклад, для заповнення сектора на диску).
Файли і функції файлової системи у мові С
У мові С файлом може бути все що завгодно, починаючи з дискового файлу і закінчуючи терміналом або принтером. Потік пов'язують з певним файлом, виконуючи операцію відкриття.
Як тільки файл відкритий, можна проводити обмін інформацією між ним і програмою. При відкритті файлу покажчик поточної позиції у файлі встановлюється в початок. При читанні з файлу (або записі в нього) кожного символу покажчик поточної позиції збільшується, забезпечуючи просування по файлу.
Файл від'єднується від певного потоку (тобто розривається зв'язок між файлом і потоком) за допомогою операції закриття.
У кожного потоку, пов'язаного з файлом, є керуюча структура, яка містить інформацію про файл, вона має тип FILE. У цьому блоці управління файлом ніколи нічого міняти не можна.
Для роботи з файловою системою існує заголовочний файл <stdio.h>. Часто використовувані функції файлової системи С такі (табл. 8.1 ).
Таблиця 8.1 – Функції для роботи з файловою системою
| Назва | Що робить | 
|---|---|
| fopen() | Відкриває файл | 
| fclose() | Закриває файл | 
| putc() | Записує символ у файл | 
| fputc() | Те саме, що і putc() | 
| getc() | Читає символ з файлу | 
| fgetc() | Те саме, що і getc() | 
| fgets() | Читає рядок з файлу | 
| fputs() | Записує рядок у файл | 
| fseek() | Встановлює покажчик поточної позиції на певний байт файлу | 
| ftell() | Повертає поточне значення покажчина у файлі | 
| fprintf() | Для файлу те саме, що printf() для консолі | 
| fscanf() | Для файлу те саме, що scanf() для консолі | 
| feof() | Повертає значення true (істина), якщо досягнуто кінець файлу | 
| error() | Повертає значення true, якщо виникла помилка | 
| rewind() | Встановлює покажчик поточної позиції на початок файлу | 
| remove() | Знищуєт файл | 
| fflush() | Дозапис потоку у файл | 
Заголовок <stdio.h> надає прототипи функцій введення/виведення і визначає наступні три типи: size_t, fpos_t і FILE. size_t і fpos_t представляють собою певні різновиди такого типу, як ціле без знака. А про третій тип, FILE, розповідається в наступному розділі.
Крім того, в <stdio.h> визначається декілька макросів. Серед них:
NULL, EOF, FOPEN_MAX, SEEK_SET, SEEK_CUR і SEEK_END.
Макрос NULL визначає порожній (null) покажчик. Макрос EOF часто визначається як -1, і є значенням, що повертається тоді, коли функція введення намагається виконати читання після кінця файлу. FOPEN_MAX – ціле значення, рівне максимальному числу одночасно відкритих файлів. Інші макроси вико ристовуються разом з fseek(), що виконує операції прямого доступу до файлу.
Щоб оголосити змінну-покажчик файлу, пишуть:
FILE *fp;
Функція fopen() відкриває потік і пов'язує з цим потоком певний файл. Потім вона повертає покажчик цього файлу:
FILE *fopen (const char *ім'я_файлу,const char *режим);
Рядок "режим", визначає, яким чином файл буде відкритий. Рядки, подібні "r+b" можуть бути представлені і у вигляді "rb+" (табл. 8.2).
Таблия 8.2 – Режими відкриття файлів
| Режим | Що означає | 
|---|---|
| r | Відкрити текстовий файл для читення | 
| w | Створити текстовий файл для запису | 
| a | Додати в кінець текстового файлу. Якщо файл не існує, то він буде створений. Всі нові дані, які записуються в нього, будуть додаватися в кінець файлу. | 
| rb | Відкрити двійковий файл для читання | 
| wb | Створити двійковий файл для запису | 
| ab | Додати в кінець двійкового файла | 
| r+ | Відкрити текстовий файл для читання/запису. Вміст залишиться недоторканим. Якщо файлу не існує, то він створений не буде. | 
| w+ | Створити текстовий файл для читання /запису. Якщо файл не існує, то він буде створений. Якщо файл вже існує, то відкриття призведе до втрати його вмісту, а в режимі r+ він залишиться недоторканим | 
| a+ | Додати в кінець текстового файлу або створити його для читання/запису | 
| r+b | Відкрити двійковий файл для читання /запису | 
| w+b | Створити двійковий файл для читання /запису | 
| a+b | Додати в кінець двійкового файла або створити його для читання /запису | 
Робота з файловими потоками
У мові С++ введення/виведення описується як набір класів, описаний в заголовному файлі iostream.h. Аналогами потоків stdin, stdout, stderr є класи cin, cout і cerr. Ці три потоки відкриваються автоматично. Потік cin пов'язаний з клавіатурою, а cout, cerr – з дисплеєм.
Файл <fstream.h> визначає класи ifstream і ofstream, за допомогою яких програма може виконувати операції файлового введення/виведення. Для відкриття файлу на введення/виведення оголошують об'єкт типу ifstream/ ofstream, передаючи конструктору цього об'єкта ім'я необхідного файлу:
ofstream myOutput ("FileOut.EXT");
ifstream myInput ("FileIn.EXT");
Після того, як програма відкрила файл для введення або виведення, вона може читати або писати дані, використовуючи оператори: "<<" – для занесення (запису) в потік; ">>" – для вилучення (читання) з потоку.
        char word [64];
            while (!myInput.eof ())
            {     myInput >> word;        // зчитуэмо слово (до пробілу)
            cout <<word <<endl;     // виводимо на екран
              myOutput << word; // записуємо у файл
            }
Більшість програм читають вміст файлу, поки не зустрінеться кінець файлу. Визначити кінець файлу можна за допомогою функції eof().
Для введення або виведення символів у файл або з файлу використовую функції get() і put().
       char letter;
            while (!myInput.eof ())
            {     letter = myInput.get(); // зчитуємо з файлу
cout <<letter; // виводимо на екран
              myOutput.put(letter);         // записуємо у файл
            }
Для зчитування цілого рядка використовують функцію getline():
        char line [80];
            while (!myInput.eof ())
            {     myInput.getline (line,sizeof (line));
            cout <<line <<endl;
}
Для перевірки помилок можна використовувати функцію fail():
        if (myInput.fail ())
            {     cerr <<"Помилка відкриття FileIn.txt" <<endl;
                  exit (1);
            }
Якщо програмам необхідно вводити або виводити такі дані, як структури або масиви, можна використовувати методи read() і write().
         myInput.read (buffer, sizeof (buffer));
            myOutput.write (buffer, sizeof (buffer));
Якщо програма завершила роботу з файлом, його слід закрити за допомогою функції close().
Для того, щоб операції введення/виведення виконувалися не з початку файлу, можна використовувати інші режими відкриття файлів (табл. 8.3).
Таблиця 8.3 – Режими відкриття файлових потоків
| Режим відкриття | Призначення | 
|---|---|
| ios::app | Відкриває файл в режимі додавання, встановлюючи файловий покажчик на кінець файлу | 
| ios::ate | Встановлює файловий покажчик на кінець файлу | 
| ios::in | Вказує відкрити файл для введення. | 
| ios::nocreate | Якщо й файл не існує, не створювати файл і повернути помилку | 
| ios::noreplace | Якщо файл існує, операція відкриття повинна бути перервана и повинна повернути помилку | 
| ios::out | Вказує відкрити файл для виведення. | 
| ios::trunc | Перезаписує вміст існуючого файлу | 
| ios::binary | Робота з файлом у двійковому вигляді | 
Наприклад,
    ifstream myFile ("Filename.txt", ios::out | ios::noreplace);
Для читання і запису даних будь-якого типу, тип яких може займати більше 1 байта, у файловій системі мови С є дві функції: fread() і fwrite().
size_t fread(void *buf,size_t count,size_t k,FILE *pf);
size_t fwrite(const void *buf,size_t count,size_t k,FILE *pf);
Функція fread() повертає кількість прочитаних елементів. Якщо досягнуто кінець файлу або сталася помилка, то повернуте значення може бути менше, ніж лічильник. А функція fwrite() повертає кількість записаних елементів. Якщо помилки не було, то повернений результат буде дорівнює значенню лічильник. Одним з найбільш корисних застосувань функцій fread() і fwrite() є читання і запис даних користувача типів. Наприклад, якщо визначена структура
      struct struct_type {
            float balance;
            char name [80];
      } Cust;
то наступний оператор записує вміст Сust у файл, на який вказує fp:
fwrite (&cust, sizeof (struct struct_type), 1, fp);
В системі введення/виведення мови С є функції fprintf() і fscanf():
int fprintf (FILE * pf, const char *str, ...);
int fscanf (FILE * pf, const char *str, ...);
Приклади програм для робот из файлами
Приклад 1. Введення інформації з клавіатури і виведення на диск.
int main (int argc, char * argv [])
{
FILE * fp;
char ch;
if (argc! = 2)
            {
                 printf ("Ви забули ввести ім'я файлу. \n");
                  exit (1);
            }  
if ((fp = fopen (argv [1], "w "))== NULL)
            {
                  printf ("Помилка при відкритті файлу. \n");
                  exit (1);
            }
            do {  ch = getchar ();
                   putc (ch, fp);
            } while (ch! ='$');
fclose (fp);
return 0;
}
Приклад 2. Програма читає текстовий файл і виводить його на екран
int main (int argc, char * argv [])
{
FILE * fp;
char ch;
     if (argc! = 2) {
              printf ("Ви забули ввести ім'я файлу. \n");
              exit (1);
        }
        if ((fp = fopen (argv [1], "r "))== NULL) {
              printf ("Помилка при відкритті файлу. \n");
              exit (1);
        }
        ch = getc (fp); /* читання одного символу */
        while (ch! = EOF) {
              putchar (ch); /* виведення на екран */
              ch = getc (fp);
        }
        fclose (fp);
  return 0;
}
Приклад 3. Програма вводить рядок з клавіатури, записує у файл, а потім читає файл і виводить його вміст на екран
int main (void)
{ char str[80];
FILE *fp;
if ((fp = fopen ("TEST", "w+"))== NULL) {
printf ("Помилка при відкритті файлу. \n");
exit (1);
}
do { printf("Введіть рядок (порожній – для виходу): \n");
gets(str);
strcat(str, "\n"); /* введення роздільник рядків */
fputs(str, fp);
} while(*str != '\n');
/* Тепер виконується читання і відображення файлу */
rewind(fp); /* встановити покажчик на початок файлу */
while (!feof(fp))
            {     fgets (str,79,fp);
                  printf (str);
            }
            return 0;
      }
Приклад 4. Використання форматованого введення-виведення у файл
int main (void)
{
FILE *fp;
char s[80];
int t;
if ((fp = fopen ("test", "w")) == NULL)
        {     printf ("Помилка відкриття файлу. \n");
                  exit (1);
            }
            printf ("Введіть рядок і число:");
            fscanf (stdin, "%s%d",s,&t);   // читати з клавіатури 
            fprintf (fp, "%s%d",s,t);           // писати в файл
            fclose (fp);
            if ((fp = fopen ("test", "r")) == NULL)
        {     printf ("Помилка при відкритті файлу. \n");
                  exit (1);
            }
            fscanf (fp, "%s%d",s,&t);           // читання з файлу 
            fprintf (stdout, "%s%d",s,t); // виведення на екран 
            return 0;
      }

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

Варіанти індивідуальних завдань
Задача 1. Взявши за основу лабораторну роботу №5, змінити код програми таким чином, щоб:
– вхідні дані вводилися не з клавіатури, а з файлу,
– результати виконання виводились і на екран, і у файл.
Задача 2. Взявши за основу лабораторну роботу №7(задача 1), змінити код програми таким чином, щоб:
– вхідні дані (поля структури) вводилися з клавіатури і після введення записувалися у файл (окрема функція);
– програма мала можливість дописувати дані у файл (окрема функція);
– дані з файлу виводились на екран (окрема функція);
– результати виконання другого підпункту виводились на екран і у файл.
При цьому виконати завдання задачі №2 у двох варіантах:
1) за допомогою класів потоків ofstream та ifstream;
2) за допомогою структури FILE та функцій роботи з нею.
* Задача 4. Одним за найпростіших в реалізації та найшвидших методів шифрування є накладання гами – послідовності псевдовипадкових чисел – шляхом виконання операції побітового додавання за модулем 2 (також відомої як exclusive or чи скорочено XOR).
Розробіть програму для зашифрування та розшифрування файлів з довільним розширенням, використовуючи як ключ, на основі якого генерується послідовність псевдовипадкових чисел, номер варіанту.

Контрольні питання
- Дати поняття потоків і файлів. Який між ними зв’язок?
 - Які види потоків бувають? Пояснити різницю між ними.
 - Охарактеризувати стандартні потоки введення-виведення.
 - Яким чином можна оголосити і відкрити файл?
 - Шо таке режими відкриття файлів і які режими ви знаєте?
 - Наведіть приклади форматованого запису у файл і читання з нього.
 - Яким чином можна встановлювати покажчик файлу у задану позицію?
 - Яким чином можна здійснювати запис і зчитування блоками?
 - Чи можна одночасно використовувати файл для запису і читання?
 - Що таке файлові потоки? Які поняття програмування забезпечують роботу з ними? Назвіть їх.
 - Основні функції роботи з файловими потоками.
 - Які режими доступу до файлових потоків ви знаєте?
 - Яким чином можна виводити у файлові потоки і вводити великі обсяги даних (структури, масиви)?
 - Як можна перевірити, чи закінчився файл, і наявність помилок?
 - Наведіть приклади запису і читання по символах, по словах, по рядках та блоками довільного розміру.