Nickolay.info. Обучение. Учебник по Паскалю. Глава 22

22. Записи. Бинарные файлы

 

Как и массивы, записи относятся к составным типам данных. Запись состоит из фиксированного числа элементов, называемых полями. Каждое поле представляет собой объект простого типа данных -- как правило, строку или число. Существенно то, что в одну запись могут входить поля различного типа, в отличие от массива, все элементы которого однотипны. Например, записью можно считать строку экзаменационной ведомости:

Борисова Ю.А.     4    5    5

Данная запись состоит из четырех полей: одно поле -- строкового типа (фамилия и инициалы студента), остальные три поля -- числовые (оценки студента по трем дисциплинам).

Описанию переменной типа запись предшествует определение типа с помощью оператора type. Для нашей записи это описание могло бы выглядеть следующим образом:

type zap = record

 fam: string;

 m, inf, h: integer;

end;

Здесь zap -- имя нового типа данных, а fam, m, inf и h -- имена полей записи. Служебные слова record ... end в данном случае играют роль операторных скобок, внутри которых записываются поля записи с указанием их типов.

Не очень удобно то, что для каждой дисциплины введено отдельное поле. Введем тип данных student, где эта проблема решена использованием в качестве поля массива balls (баллы студента по трем дисциплинам):

type student = record

 fam: string;

 balls: array [1..3] of integer;

end;

После сделанного таким образом определения в разделе описания переменных можно объявить переменную типа "запись":

var str: student;

или массив таких переменных:

var students: array [1..20] of student;

Для обращения к отдельным полям переменной типа запись используется селектор записи -- символ точки, разделяющий наименования записи и поля:

str.fam

- обращение к полю "фамилия" записи str;

students[1].fam

- обращение к полю "фамилия" первого элемента массива записей students;

students[i].balls[2]

- вторая оценка i-го элемента массива записей students.

С полем записи, как и с элементом массива, разрешены все допустимые в языке операции над переменной соответствующего типа.

Для того чтобы узнать объем в байтах, занимаемый записью в оперативной памяти или на жестком диске, следует воспользоваться стандартной функцией sizeof, передав ей в качестве аргумента имя типа записи:

sizeof (student) -- вернет размер памяти в байтах, занимаемый одной записью типа student.

Чаще всего записи используются при работе с базами данных, хранящимися на жестком диске. Мы уже умеем читать и записывать текстовые файлы, но хранение записей в текстовом файле, как правило, неудобно и требует каждый раз сложного разбора прочитанных строк. Поэтому для эффективной работы с файлами записей следует познакомиться с остальными типами файловых переменных Паскаля.

В общем виде файл данных в Паскале представляет собой совокупность однотипных компонент. В зависимости от способа объявления файловой переменной можно выделить три вида файлов.

·       Типизированные файлы. Для них тип компонент указывается непосредственно в описании файловой переменной. Описание в этом случае имеет вид: var файловая_переменная: file of тип_компонент_файла;

Например, если компоненты файла имеют тип записи zap, то разделе описания переменных можно объявить файловую переменную следующим образом:

var f: file of zap;

Естественной "порцией данных" для такого файла будет одна запись типа zap, что очень удобно. Типизированный файл может быть объявлен и как совокупность записей простого типа:

var f2: file of real;

Здесь объявлен файл, содержащий вещественные числа во внутреннем представлении. Размер одной записи такого файла будет равен 6 байт (размер памяти, отводимый под величину типа real).

·       Нетипизированные файлы. Задаются стандартным типом file без указания типа компонент, например:

var f: file;

Чтение и запись данных в такой файл может осуществляться произвольными "порциями" данных, что удобно, например, при организации временных и буферных файлов. Рассмотрение нетипизированных файлов выходит за рамки нашего курса.

·       Текстовые файлы. Знакомые нам по гл. 21 текстовые файлы задаются стандартным типом text, например:

var f: text;

Компонентами текстового файла являются строки переменной длины. После ввода каждой строки нажимается клавиша Enter. Исторически сложилось так, что при этом в конец каждой строки дописывается два невидимых символа: символ с кодом 13 (CR, возврат каретки) и символ с кодом 10 (LF, перевод строки). Доступ к строкам осуществляется последовательно, начиная с первой. Число строк в текстовом файле может быть произвольным. Последним символом файла может быть специальный маркер EOF (End Of File) с кодом #26.

Существенно подчеркнуть то, что первый и второй вид файлов -- бинарные, а не текстовые:

Текстовый файл содержит только алфавитно-цифровые символы и ряд специальных (такие, как возврат каретки, перевод строки, табуляция). Содержимое файла непосредственно доступно для чтения в любом редакторе текста или в окне консоли.

Бинарный файл содержит произвольные символы, чаще всего он представляет собой набор данных в машинном коде и недоступен для непосредственного чтения.

Например, следующая несложная программа объявляет бинарный файл, состоящий из вещественных чисел и записывает в него числа от 1 до 10 включительно:

var f:file of real; r:real;

begin

 assign (f,'real.dat');

 rewrite (f);

 r:=1;

 while r<11 do begin

  write (f,r); r:=r+1;

 end;

 close (f);

end.

Так как размер величины типа real в памяти равен 6 байтам, полученный файл real.dat будет иметь размер 60 байт и содержать внутренние машинные представления указанных чисел (рис. 22.1).

 

Рис. 22.1. Бинарный файл

 

Никакого "текстового" смысла в этой записи нет, чтобы ее интерпретировать, нужно открыть файл в редакторе, поддерживающем 16-ричное "машинное" представление чисел или дамп (рис. 22.2).

 

Рис. 22.2. Шестнадцатеричный дамп бинарного файла

 

В этом представлении видно, что каждое число занимает по 6 байт.

Переписав программу так, чтобы числа писались в текстовом представлении, получаем следующее:

var f:text; r:real;

begin

 assign (f,'real.txt');

 rewrite (f);

 r:=1;

 while r<11 do begin

  write (f,r); r:=r+1;

 end;

 close (f);

end.

Файл real.txt состоит из одной строки (так как мы писали только оператором write), в этой строке приведены вещественные числа в экспоненциальной форме (поскольку мы не указывали ширину и точность вывода). Файл изображен на рис. 22.3.

 

Рис. 22.3. Текстовый файл

 

Размер файла real.txt равен 170 байт, он текстовый и его можно открыть в "Блокноте" Windows.

Важно усвоить и то, что для бинарных файлов не имеет смысла понятие "строки текста". Поэтому чтения и запись при работе с бинарными файлами осуществляются только процедурами read и write, но не readln и writeln.

Все изученные нами ранее приемы и средства работы с файлами (кроме цикла с функцией eof), вообще говоря, применимы лишь к текстовым файлам, так как существенно зависели от понятия строки данных. Для работы с бинарными файлами необходимы иные средства, позволяющие удобно перемещаться по их записям.

Считается, что в каждый момент времени в файле установлен указатель, определяющий текущую позицию чтения или записи. После чтения или записи компоненты файла указатель сдвигается на его следующую компоненту. Узнать текущую позицию указателя или установить его новое положение позволяют следующие стандартные подпрограммы:

function Filesize(var F) : longint;

 -- возвращает текущий размер файла в компонентах (не байтах! Последнее верно только для file of byte). Параметр F -- файловая переменная. Filesize(F) возвращает число компонентов в F. Если файл пустой, то Filesize(F) возвращает ноль. В режиме {$I-} функция IOResult вернет ноль, если операция была успешна, иначе она вернет отличный от ноля код ошибки. Функция Filesize не может использоваться для текстовых файлов. Файл должен быть открыт;

function FilePos(var F) : longint;

 -- возвращает текущую позицию указателя файла. Параметр F -- файловая переменная. Если указатель текущей позиции файла находится в его начале, то FilePos(F) возвращает ноль. Если указатель текущей позиции файла находится в конце файла, то есть, Eof(F)=true, то значение FilePos(F) равно значению Filesize(F). В режиме    {$I-} функция IOResult вернет ноль, если операция была успешна, иначе она вернет отличный от ноля код ошибки. Функция FilePos не может использоваться для текстовых файлов. Файл должен быть открыт;

procedure seek(var F; N : longint);

 -- перемещает текущий указатель позиции файла на определенный компонент. F -- переменная любого файлового типа за исключением текстового, N -- выражение типа longint. Указатель позиции файла F перемещается на номер компонента N. Номер первого компонента файла равен нулю (соответственно, последнего -- p-1, где p -- общее число компонент в файле). Чтобы расширить файл, вы можете передвинуть указатель на один компонент за последний компонент в файле. То есть, оператор seek(F, Filesize(F)); перемещает текущий указатель позиции файла на конец файла. В режиме {$I-} функция IOResult вернет ноль, если операция была успешна, иначе, она вернет отличный от ноля код ошибки. Процедура  seek не может быть использована для текстовых файлов. Файл должен быть открыт.

Сама процедура чтения или записи бинарного файла состоит из тех же шагов, что для текстового: файл связывается с файловой переменной оператором assign, затем открывается в режиме reset или rewrite, чтение и запись выполняются операторами read и write соответственно, по завершении работы с файлом его следует закрыть оператором close.

Для бинарных файлов запрещен режим открытия append, зато режим reset позволяет как читать записи файла, так и писать в него. Режим rewrite с бинарными файлами используется так же, как с текстовыми -- то есть, только при необходимости переписать файл заново.

Как для текстовых, так и для бинарных файлов могут понадобиться следующие стандартные средства:

procedure Erase(var F);

- стирает внешний файл с диска. Параметр F -- файловая переменная любого файлового типа. Внешний файл, связанный с переменной F удаляется. В режиме {$I-} функция IOResult вернет ноль, если операция была успешна, иначе она вернет отличный от ноля код ошибки. Перед выполнением данной процедуры файл надо закрыть, если он ранее был инициализирован процедурами reset, rewrite или append.

procedure rename(var F; NewName:string);

- переименовывает внешний файл. Параметр F -- переменная любого файлового типа. Внешний файл, связанный с переменной F переименовывается в NewName. Дальнейшие операции над файлом F происходят уже с внешним файлом с новым именем. В режиме {$I-} функция IOResult вернет ноль, если операция была успешна, иначе, она возвращает отличный от ноля код ошибки.

Развернутый пример на работу с файлом записей приведен в Приложении 4 (листинг 4). Его тщательное изучение поможет вам освоить основные приемы работы с бинарными файлами. Программа, приведенная в листинге -- учебная, поэтому "узких мест", вызывающих сомнения с точки зрения профессиональности кода, в ней немало, по возможности они закомментированы с соответствующими пояснениями.

Рейтинг@Mail.ru
вверх гостевая; E-mail