Стандарт ТР вводить особливий файловий тип, який являється по суті
узагальненим файловим типом. Позначення складається тільки зі слова
file без вказування типу компонентів.
Безтиповий тип - це дуже потужний засіб роботи з файлами, так як він
дозволяє маніпулювати з даними не замислюючись про їх типи. З його
допомогою, можна записувати на диск довільні ділянки робочої пам'яті,
та зчитувати їх в пам'ять диску. Можливо перетворювати дані, які
зчитуються із безтипового файлу в любий формат засобом приведення
типів.
Введення-виведення у безтипові файли здійснюється спеціальними
процедурами:
BlockRead та BlockWrite.
Крім того розширюється синтаксис процедур
Reset і Rewrite. В решті принципи роботи залишаються ті ж самі, як і
при роботі з типізованими файлами. Перед використанням файлова
змінна повинна бути зв'язана з конкретним фізичним файлом через виклик оператора Assign. Файл повинен бути відкритим для читання або запису за допомогою виклику процедури Reset(f) і Rewrite(f). Після закінчення роботи файл повинен бути закритим процедурою Close. Відкриваючи безтиповий файл для роботи, користувач неявно встановлює розмір буферу передачі даних, який дорівнює 128 байтів. Однак, можливо явним способом вказати інший розмір буфера (чим він більший, тим швидше проходить введення-виведення), виходячи з ресурсів пам'яті і зручності роботи з даними. Для задання буферу необхідно після оператора Assist відкрити файл розширеним записом процедур:
Reset(Var f:file; BufSize:word);
Rewrite(Var f:file; BufSize:word);
Параметр BufSize задає число байт, зчитаних із файлу за одне звернення до нього, або тих, що записуються в нього. Чим більше значення BufSize, тим швидше проходить обмін даними між носієм файлу (диск) і ОП машини, але тим більша і затрата пам'яті, тому, що саме в пам'яті знаходиться буфер файлу.
Мінімальний блок, який може бути записаний чи прочитаний із файлу -це один байт. Щоб задати його необхідно встановити саме таку величину буфера при відкритті файлу. Максимальний розмір блоку не може перевищувати 64 Кб.
Під час відлагодження програми в ТР можна перевірити розмір буфера, помістивши в вікно перегляду або в вікно аналізу файлову змінну £, що приведена до типу FileRec. (Для цього необхідно підключення модуля DOS)
Для зчитування або запису даних в безтипових файлах застосовувати стандартні процедури Read і Write не рекомендується, їх заміняють процедурами:
BlockRead(Var f:file; VarDestin; count: word[; Readln: word]);
BlockWrite (Var f:file; Vаr Sourse; count: word[; WriteOut: word]);
Ці процедури здійснюють читання в любу змінну Destin і запис із змінної Sourse компонентів файлу, або його рядків чи блоків, що складаються з тієї кількості байтів, яка визначена для буфера файлу f. Якщо count > 1, то за одне звернення буде зчитано count ємностей буферу. Значення count <l не має змісту. Завжди повинна виконуватися умова:
Соипt*Розмір_буферу < 64 Кбайт
Необов'язковий параметр Readin повертає число блоків (буферів), зчитаних поточною операцією BlockRead. Аналогічний параметр WriteOut процедури BlockWrite після кожної операції запису показує число блоків, що записане в даний файл цією операцією.
Якщо операції запису чи читання пройшли успішно, це значить що Readin і WriteOut будуть дорівнювати відповідним значенням параметрів Count, але якщо виник збій при введенні-виведенні і замовлене число блоків не перенеслось, то параметри Readin і WriteOut будуть містити ціле число вдало перенесених блоків (невдача посередині блоку практично рівносильна відміні його читання чи запису). Таким чином, ці параметри можуть використовуватись для контролю виконання операцій BlockRead і BlockWrite.
Приклад:
Var f1,f2:file; {файлові змінні}
Readln, WriteOut: word; {змінні контролю}
Destin, Source: word;
Begin
……………..
BlockRead(fl, Destin, 3, Readln);
If readin 3 then {обробка помилки читання}
…………….
BlockWrite(f2t Source, 4, WriteOut) ;
If WriteOut< > 4 then {обробка помилки запису}
…………….
end.
Якщо при виклику BlockRead останній параметр не вказано, то неможливість зчитати задане число блоків викличе помилку введення-виведення і зупинку програми. Процедури введення-виведення BlockRead і BlockWrite не мають списків введення та виведення, оскільки не визначений тип компонентів файлу. Замість них в викликах присутні безтипові змінні Destin і Source. Адреса початку змінної в пам'яті відповідає адресі пам'яті, починаючи з якої задану кількість байт буде виведено в файл при запису чи вміщено в пам'ять із файлу при читанні. Передаючи змінну процедурі, ми завжди передаємо адресу її змісту, точніше першого байту її значення.
Якщо змінна х масив Var x: array[1..10]of... ,то виклик BlockWrite або BlockRead буде приймати в ній за точку початку відліку блоку перший елемент масиву. Можна більш явно в виклику вказати початок блоку x як х[1]. Але якщо підставляти х[5], то перелік блоку буде вестись уже, починаючи з п'ятого елементу масива.
Особливо обережно треба поводитися зі вказівниками при підстановці їх у BlockWrite і BlockRead. Вказівники повинні бути розіменовані для того, щоб вказувати на дані, а не на місце в пам'яті де зберігається сам вказівник.
Якщо визначений вказівник Р
Type Dim =array[0.. 999] of real;
Var p:^Dim; {вказівник на масив} f:file;
то після створення динамічного масиву Р^, викликом процедури New(p) і його заповнення, він може бути записаний в файл наступним чином:
Assign(f, 'dimfile.dat') ; {зв'язує файли}
Rewrite(f,Sizeof(Dim)); {відкриває файл для читання}
BlockWrite(f,p^ ,1); {записує масив у файл, зсилка р^ розіменована}
Якщо помилково записати р замість р^ то процедура буде працювати, але збережеться у файлі кусок пам'яті, починаючи з Addr(p), який зовсім не дорівнює адресі динамічного масиву Addr(p^). Щоб прочитати згодом записаний масив із файлу, потрібно змінити напрям виведення даних ' (Місце під р повинно бути зарезервовано):
New (р);
Assign(f, 'dimfile.dat');
Reset(f, SizeOf(dim)); {відкриття файлу для читання}
BlockReadln(f, р^ , 1); {читання масиву із файлу}
Close(f);
End.
Перед читанням блоку в динамічну змінну (р^), вона повинна бути коректним чином створена (через виклик New або GetMem, в протилежному випадку наслідки будуть непередбачені). Блочний спосіб роботи з файлами дуже ефективний по часу, і якщо програма використовує великі масиви попередньо обчислювальних констант, то може виявитись більш вигідним винести їх обчислення в іншу програму, котра їх збереже на диску, а в розрахунковій програмі просто встановити, оператори блочного читання вже розрахованих значень. В таких випадках можна навіть виграти на особливостях компілятора ТР. Зазвичай при компіляції програми пам'ять під статичні масиви відводиться в порядку їх слідування в описі. Якщо описані:
Var A,B,C:Array[1..2000] of real;
то їх елементи ставляться в один суцільний ланцюжок.
Addr(B) =Addr(A) +SizeOf(A);
{вказівник на початок об 'єктa в пам 'яті}
Addr(C) =Addr(B)+SizeOf(B);
Це вірно не лише для масивів, але і для любих статичних структур, крім об'єктів: пам'ять в межах блоку опису змінних відводиться послідовно по мірі слідування. Використовуючи цей факт, можна записати або зчитати блоком відразу кілька стуктур даних, прийнявши за початок блоку першу з них:
Assign(f, 'ABC.dat');
Rewrite(f, SizeOf(A)); {відкриває файл та записує в нього 3 блока відразу}
BlockWrite(f,A,3);
Close(f);
End.
Іншою, більш специфічною областю застосування безтипових файлів є робота з системними областями IBM, в тому числі з відеопам'яттю.