Робота з файлами
Можливо у програмах використовувати набори даних, підготованих заздалегідь, що зберігаються у файлі. Це називають зчитуванням даних із зовнішнього файлу (чи просто з файлу). Такий спосіб знаходить широке застосування у процесі обробки інформаційних масивів дуже великого обсягу, коли інтерактивний режим стає занадто обтяжним і малоефективним.
Іншим мотивом використання файлів є те, що файл може бути створений якоюсь іншою програмою. Тобто, файл стає сполучною ланкою між різними задачами. Крім того, якщо вхідні дані надходять у програму із файлу, то присутність користувача в момент виконання програми є необов’язковою.
Файл – це іменована область зовнішньої пам’яті комп’ютера, яка містить логічно пов’зані дані.
У мові Pascal використовується три типи файлів: типізований, нетипизований та текстовий, які в програмі визначаються наступним чином:
: text;
: file of тип;
: file.
Приклад:
ТУРЕ { запис даних про студента }
RecFile = RECORD { ознака початку запису }
Fam, Name, Otch : string[15];
GodR : word;
Ngrup : string[10];
END; { кінець запису }
VAR F1 : Text;
F2 : File of byte;
F3 : File of RecFile;
F4 : File;
Buf : RecFile; {_2буфер уведення-висновку_0, у який зчитуються дані компоненти файлу}
У цьому фрагменті програми F1- файлова змінна текстового типу, F2 і F3 – типізовані файлові змінні, причому F2 може містити тільки однобайтові елементи, а у файлі F3 кожний компонент уявляє із себе “запис” із трьох елементів, F4- файлова змінна нетипизованого типу.
Під час роботи з файлами необхідно привести у відповідність файлову змінну й ім’я файлу, з яким він зберігається на зовнішньому носії. З цим іменем працює операційна система. Відповідність установлюється за допомогою процедури:
ASSIGN (F, Name);
тут F- файлова змінна; Name- це повне ім’я файлу, яке містить у собі шлях доступу, безпосереднє ім’я файлу і розширення.
Наприклад:
ASSIGN (F1, ‘a:\Tp5\DAT\St629.DAT’);
ASSIGN (F2, ‘Dannye.DAT’).
Якщо не зазначений шлях до файлу, то чи запис зчитування здійснюється в поточний директорій.
Як ім’я файлу в процедурі ASSIGN можна вказувати логічний пристрій з наступного списку: CON, PRN, AUX.
CON – це ім’я консолі. На персональному комп’ютері (ПК) під консоллю розуміється сукупність двох пристроїв: клавіатури і дисплея. Клавіатура використовується для введення інформації, а дисплей для виводу.
PRN – це стандартне ім’я принтера. В ОС PRN стандартно призначається LPT1. У модулі Printer Паскаля оголошена файлова перемінна Lst. Тому для відображення даних на принтер досить підключити модуль Printer до програми, а в процедурах Write і Writeln першим аргументом записати ім’я файлової перемінної Lst.
Приклад висновку інформації без використання модуля Printer:
VAR F : File;
BEGIN
Assign(F, PRN);
ReWrite(F);
Writeln(F, ‘Приклад використання Л.У. – PRN’);
Close(F);
END.
AUX – це ім’я комунікаційного порту. Звичайно їх буває два в ПК: СОМ1 і СОМ2. Стандартно AUX призначається СОМ1. Цей порт звичайно використовується для підключення нестандартних пристроїв. Наприклад, “миші”, графобудівника і т.ін.
Перш ніж почати обробку файлу необхідно виконати деякі операції по роботі з пристроєм, на якому зберігається або буде зберігатися файл. Так, наприклад, під час створення файлу необхідно: виділити область пам’яті на зовнішньому пристрої, у яку будуть записуватися дані файлу; запам’ятати ім’я файлу й адресу
цієї області.
Якщо має бути робота з файлом, що вже існує на зовнішньому носії, то необхідні наступні дії: по зазначеному імені файлу знайти адреси цього файлу; установити голівку пристрою на початок файлу.
Ця сукупність операцій називається ініціацією файлу чи “відкриттям” файлу. Ініціюється файл за допомогою процедур Reset і ReWrite. За допомогою процедури Reset ініціюється, тобто відкривається раніше створений файл. За допомогою процедури ReWrite ініціюється файл для запису, тобто файл, що створюється.
Синтаксис:
Reset( [,]);
ReWrite( [,]);
Другий аргумент вказується тільки для нетипизованих файлів. Текстові файли можна ініціювати також і процедурою Append:
Append();
У цьому випадку раніше створений файл відкривається для додавання даних у кінець файлу.
Завершивши роботу з файлом, необхідно його закрити. При закритті файлу ОС підраховує розмір файлу в байтах і запам’ятовує його. Крім того, запам’ятовується також інформація про дату і час створення файлу чи його останньої модифікації (коректування). Закриття файлу даних здійснюється процедурою Close:
Close();
При зчитуванні даних з раніше створеного файлу кінець файлу можна визначити з допомогою функції EOF:
EOF();
Ця функція має значення TRUE у процесі зчитування маркера кінця файлу. У противному випадку вона буде мати значення FALSE. Дана функція звичайно використовується для організації циклу читання всіх компонентів файлу, наприклад:
while not EOF(F1) do begin
…
…
end;
Безпосереднє введення інформації здійснюється процедурами READ і READLN, а вивід (запис) інформації – WRITE і WRITELN. Особливістю їхнього застосування до файлу є обов’язковість вказівки файлової змінної як першого параметра у списку елементів:
Read(, );
ReadLn(, );
Write(, );
WriteLn(,).
Текстовий файл – це сукупність рядків змінної довжини. Змінна довжина рядків визначає наявність маркерів, що відзначають кінець рядка. Як маркери використовуються два керуючі символу “Перехід рядка” і “Повернення каретки”, їх десяткові коди: #10, #13. Назви керуючих символів “Перехід рядка” (LF – Line Feed) і “Повернення каретки” (CR – Carriage Return) узяті за аналогією роботи з пишучою машинкою.
Кінець рядка можна визначити за допомогою функції EOLn:
EOLn();
Для запису даних у файл використовуються процедури WRITE і WRITELN:
Write(, );
Writeln(, ).
За процедурою WRITE значення даних зі списку запишуться у файл підряд без усяких роздільників. Тому програміст, використовуючи процедуру WRITE, повинний подбати про роздільники між даними, якщо вони потрібні. За процедурою WRITELN у файлі після кожного виведеного значення будуть записані ознаки кінця рядка.
Для читання даних з файлу використовуються процедури READ і READLN:
Read(, );
Readln(, );
За процедурою READ з файлу вибирається стільки символів, скільки зазначено в описі поточної змінної, що належить списку вводу. Обрана послідовність символів привласнюється поточній змінній. Ця сукупність операцій повторюється для всіх елементів списку вводу. За процедурою READLN з файлу послідовно зчитуються рядки і привласнюються змінним. Якщо обраний рядок має більшу кількість символів, ніж зазначено в описі поточної змінної, то вона обрізається до зазначеної довжини, при цьому частина інформації губиться. Тому необхідно стежити за відповідністю довжин змінних, що записуються у файл і що зчитуються з файлу.
Приклад:
Var Fio, Otch : string[15];
Name : string[10];
i : integer;
F : text;
Begin
Assign(F, ‘St629.DAT’);
ReWrite(F); { відкриття файлу для запису }
for і:=1 to 5 do { обмежимося введенням п’яти студентів }
begin
Write(‘Прізвище: ‘);
Readln(Fam);
Write(‘Ім’я: ‘);
Readln(Name);
Write(‘По батькові: ‘);
ReadLn(Otch);
Write(F, Fam, Name, Otch);
end;
close(F);
WriteLn(‘Прізвище Ім’я По батькові’);
Reset(F); { відкриття існуючого файлу }
for i:=1 to 5 do
begin
Read(F, Fam, Name, Otch);
Write(Fam:16, Name:11, Otch:15);
end;
close(F);
End.
Компоненти типізованого файлу можуть бути наступних типів:
- базового: byte, word, longint, integer, real, запис, char, string;
- базового: byte, word, longint, integer, real, запис, char, string;
структурного;
регулярного.
При цьому усі компоненти файлу мають той самий тип. Це означає, що довжина компоненти фіксована. Описується такий файл у програмі в такий спосіб :
Var
F1 : File of byte;
F2 : File of string[80];
F3 : File of real;
. . .
F : File of RecFile;
Тут F1, F2, F3, F – це файлові змінні, котрі вказують на файли, компоненти яких відповідно є типу byte, string, real і record. Читання компонентів файлу виконується процедурою:
Read(, );
Запис компонентів у файл виконується за допомогою процедури:
Write(, );
Приклад:
Var
X, Y : array[1..100] of integer; { масиви координат }
F : file of real;
i : byte;
Begin
. . .
. .
Assign(F, ‘Coor.dat’); { файл буде створюватися в поточному каталозі }
ReWrite(F); { відкриття файлу для запису }
For і:= 1 to 100 do
Write(F, X[і], Y[і]); { запис координат у файл }
Close(F);
End.
У наведеному фрагменті програми координати записані послідовними парами X, Y. При такій організації файлу відбувається часте звертання до зовнішнього носія, це призводить до уповільнення роботи програми, що особливо помітно у процесі роботи з великими обсягами даних. Тому рекомендуються дані записувати у файл і зчитувати з файлу великими блоками, приблизно кратним 512 байтам. Відповідно до цього модифікуємо програму в такий спосіб:
Type
Coord = array[1..100] of integer; { масиви координат }
. . .
Var X, Y : Coord; { масиви координат }
F : file of Coord; { файл регулярного типу }
i : byte;
Begin
. . .
. . .
Assign(F, ‘Coor.dat’); { файл буде створюватися в поточному каталозі }
ReWrite(F); { відкриття файлу для запису }
Write(F, X); { запис масиву координат Х в файл }
Write(F, Y); { запис масиву координат Y у файл }
Close(F);
End.
Тепер у файлі спочатку записані 100 координат Х, а потім 100 координат Y. Дані файлу ми записали двома великими блоками по 600 байтів кожний. Зчитування координат з файлу виконується аналогічно, у програмі потрібно замість процедури REWRITE використовувати процедуру RESET, а замість пропозиції WRITE використовувати пропозицію READ.
Добре структуровані дані, наприклад, дані про який-небудь об’єкт, зручно описувати типом “запис”. У цьому випадку компонента файлу буде структурного типу.
Приклад:
Type RecFile = record { запис даних по студенту }
Fam, Name, Otch : string[15];
GodR : word;
NGrup : string[10];
end;
Var i : integer;
Buf : RecFile;
FilStud : file of RecFile;
Begin
Assign(FilStud, ‘Stud.dat’);
ReWrite(FilStud);
WriteLn(‘Уведіть дані по студентах:’);
For і:= 1 to 10 do { обмежимося 10 записами }
begin
Write(‘Прізвище: ‘);
Readln(Buf.Fam);
Write(‘Ім’я: ‘);
Readln(Buf.Name);
Write(‘По батькові : ‘);
Resdln(Buf.Otch);
Write(‘Рік народження : ‘);
Readln(Buf.God);
Write(‘N групи : ‘);
Readln(Buf.NGrup);
{ запис даних у файл }
Write(FilStud, Buf); { Buf – звертання до всього запису }
end;
Close(FilStud);
End.
Читання компонентів типізованого файлу можна здійснювати як послідовним, так і прямим методом доступу. Послідовний доступ – це є доступ до компонента файлу тільки після перебору всіх попередніх. Прямий доступ – це є доступ відразу до зазначеного компонента.
Тому що типізовані файли мають компоненти фіксованої довжини, існує можливість організувати прямий доступ. Для організації прямого доступу до компонент файлу існують стандартні процедури Seek, FilePos, FileSize :
Seek(,);
FilePos();
FileSize().
Процедура Seek здійснює прямий доступ до будь-якого компонента файлу. Тут – позиція покажчика компонент файлу. Вона може
приймати наступні значення:
+1 – установити покажчик на наступну компонент;
-1 – установити покажчик на попередню компонент;
і – установити покажчик на i-ту компоненту.
Процедура FilePos визначає номер поточної позиції у файлі, а точніше номер поточної компоненти.
Процедура FileSize визначає розмір зазначеного файлу – кількість компонент. Нумерація компонентів починається з нуля.
Приклад:
Type RecFile = record { запис даних по студенту }
Fam, Name, Otch : string[15];
GodR : word;
NGrup : string[10];
end;
Var i : integer;
Buf : RecFile;
FilStud : file of RecFile;
Begin
Assign(FilStud, ‘Stud.dat’);
Reset(FilStud);
i:= FileSize;
WriteLn(‘У файлі ‘, і, ‘ компонент’);
Seek(FilStud, і-1);
Read(FilStud, Buf);
Buf.God:= ‘1973’;
Seek(FilStud, -1);
Write(FilStud, Buf);
Close(FilStud);
End.
Відкриваючи типізований файл процедурою RESET, можна цей файл не тільки читати, але і записувати в нього нову інформацію. При цьому файл повинний уже існувати на диску.
Нетипізовані файли можуть містити у своєму складі будь-які типи компонент. При цьому правильність запису і зчитування цих компонентів цілком покладається на програміста. Довжина компонентів може бути різною. Для відкриття нетипізованих файлів використовуються процедури Reset, ReWrite:
Reset(, );
ReWrite(, ).
Тому що за одне звертання до нетипізованого файлу можна зчитувати не одну компоненту, а менше, і тому що довжини компонентів можуть бути різні, то в процедурах Reset і ReWrite указується максимальний розмір буфера вводу-виводу в байтах.
Читання компонентів з файлу і запис їх у файл виконується процедурами BlockRead і BlockWrite:
BlockRead(, ,<<span style=””> компонент, що зчитуються за один раз >, [,]);
BlockWrite(,,, [,< компоненти, що записуються >]).