Delphi 4 для начинающих. Часть 3

Delphi 4 для начинающих. Часть 3
Разгребаясь с письмами читателей, я решил начать следующую статью с работы с файлами и строковыми переменными. На такой ход меня натолкнуло следующее письмо: "Я только начал программировать на Delphi. У меня к вам большая просьба: при создании различных программ часто приходится работать с файлами (создавать, удалять, копировать, переписывать файлы), не могли бы вы в ближайшем номере "Компьютерной газеты" мне рассказать о методах работы с файлами". Что ж, желание просящих выполняю.


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

Для использования файлов в приложении разработчику приходится решать множество задач. Основные из них – это поиск необходимого файла и выполнение с ним операций ввода/вывода.

Основные принципы и структура файловой системы мало изменились еще со времен MS-DOS. Если не принимать во внимание способы защиты файлов и организацию их хранения на уровне кластеров, то все остается без изменений вот уже скоро двадцать лет. Новые варианты файловых систем (FAT32, NTFS) не изменяют главного – понятия файла и способов обращения к нему. Поэтому современный программный код Delphi, например, для чтения данных из файла, удивительно похож на аналогичный, написанный, к примеру, на Turbo Pascal 4.0.

При организации операций файлового ввода/вывода в приложении большое значение имеет тип используемых данных. В основном это строки, но встречаются числовые данные или структурированная информация, например, записи или массивы данных. О типе хранимых в файле данных необходимо знать заранее. Для работы с файлами необходимо для начала описать тип файла. Для этого используются специальные файловые переменные. Они делятся на нетипизированные и типизированные. В Delphi имеется одна нетипизированная переменная. Для ее обозначения используется ключевое слово file :


var UntypedFile:file;


Такие файловые переменные используются для организации быстрого и эффективного ввода/вывода безотносительно к типу данных. При этом подразумевается, что данные читаются или записываются в виде двоичного массива. Для этого используются специальные процедуры блочного чтения и записи.

Типизированные файловые переменные обеспечивают ввод/вывод с учетом конкретного типа данных. Для их объявления используется ключевое слово file of, к которому добавляется конкретный тип данных. Например, для работы с двоичным файлом файловая переменная будет иметь вид:

v ar ByteFile: file of byte;


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


type Country = record

Name: String;

Capital: String;

Population: LongInt;

Square: LongInt;

end;

var CountryFile: file of Country;



Для работы с текстовыми файлами используется специальная файловая переменная TextFile или Text :

var F: TextFile;



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

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

  1. Объявить файловую переменную;
  2. При помощи функции AssignFile связать эту переменную с требуемым файлом;
  3. Открыть файл при помощи функций Append, Reset или Rewrite;
  4. Выполнить операции чтения и/или записи. При этом, в зависимости от сложности задачи и структуры данных, может использоваться целый ряд вспомогательных функций.
  5. Закрыть файл при помощи функции CloseFile .

В качестве примера рассмотрим небольшой фрагмент исходного кода программы:




var

F:TextFile;

S:string;

Begin

If OpenDlg1.Execute then AssignFile(F, OpenDlg1.fileName) else exit;

Reset(F);

While Not EOF(F) do

Begin

Readln(F,S);

Memo.Lines.Add(S);

End;

CloseFile(F);

End;




Если в диалоге OpenDlg1 был выбран файл, то его имя связывается с файловой переменной F при помощи процедуры AssignFile . В качестве имени файла всегда рекомендуется передавать полное имя файла (включая путь к нему). Как раз в таком виде возвращают результат выбора файла диалоги работы с файлами, описанные в материале прошлого номера. Затем при помощи процедуры Reset этот файл открывается для чтения и записи. В цикле осуществляется чтение из файла текстовых строк и запись их в компонент TMemo . Процедура Readln осуществляет чтение текущей строки файла и переходит на следующую строку. Цикл выполняется до тех пор, пока функция EOF не сообщит о достижении конца. По завершении цикла файл закрывается.

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


procedure Reset(var F [: File; RecSize: Word ] );
- открывает существующий файл для чтений и записи, текущая позиция устанавливается на первой строке файла;


procedure Append(var F: Text);
- открывает файл для записи информации после его последней строки, текущая позиция устанавливается на конец файла;

procedure Rewrite(var F: File [; Recsize: Word ] ); - создает новый файл и открывает его, текущая позиция устанавливается в начало файла. Если файл с таким именем уже существует, то он перезаписывается.

Чтение данных из файла выполняют процедуры Read и Readln.


procedure Read( [ var F: Text; ] V1 [, V2,...,Vn ] ); -
для текстовых файлов;


Procedure Read(F, V1 [, V2,…,Vn ] );
- для типизированных файлов.

При одном вызове процедуры можно читать данные в произвольное число переменных. Естественно, тип переменных должен совпадать с типом файла. При чтении в очередную переменную читается ровно столько байт из файла, сколько занимает тип данных. В следующую переменную читается столько же байт, расположенных следом. После выполнения процедуры текущая позиция устанавливается на первом непрочитанном байте. Аналогично работают несколько процедур Read для одной переменной, выполненных подряд.

procedure Readln([ var F: Text; ] V1 [, V2, ...,Vn ]); - считывает одну строку текстового файла и устанавливает текущую позицию на следующую строку. Если использовать процедуру без переменных, то она просто передвигает текущую позицию на следующую строку.

Процедуры для записи данных в файл Write и Writeln работают аналогично.

Для контроля за текущей позицией в файле применяются две основные функции. Функция EOF возвращает значение True, если достигнут конец файла. Функция EOLN сигнализирует о достижении конца строки. В качестве параметра в функции необходимо передавать файловую переменную.


procedure Seek(var F; N: Longint); -
обеспечивает смещение текущей позиции на N элементов. Размер одного элемента в байтах зависит от типа данных файла (от типизированной переменной).

Рассмотрим теперь режим блочного чтения и записи в файл. Он обеспечивает ввод/вывод данных между файлом и областью адресного пространства (буфером). Этот режим отличается значительной скоростью, причем скорость пропорциональна размеру одного передаваемого блока – чем больше блок, тем больше скорость.

Для реализации этого режима необходимо использовать только нетипизированные файловые переменные. Размер блока определяется в процедуре открытия файла ( Reset, Rewrite, Append ). Непосредственно для выполнения операций используются процедуры BlockRead и BlockWrite . Процедура


procedure BlockRead(var F: File; var Buf; Count: Integer [; var AmtTransferred: Integer]);


выполняет запись блока из памяти в файл. При этом параметр F содержит нетипизированную файловую переменную, связанную с нужным файлом, параметр Buf определяет переменную (число, строку, массив, структуру), в которую читаются байты из файла, параметр Count определяет число считываемых блоков, а параметр AmtTransferred возвращает число реально считанных блоков.

В блочном режиме чтения/записи размер блока необходимо выбирать таким образом, чтобы он был кратен размеру одному значению типа, который хранится в файле. Например, если в файле хранятся значения типа Double (8 байт), то размер блока может быть равен 8, 16, 24, 32 и т.д. Фрагмент исходного кода блочного чтения из такого файла выглядит так:




var

F:file;

DoubleArray: array [0..255] of Double;

Transfered:Integer;

Begin

If OpenDlg.Execute then AssignFile(F, OpenDlg.FileName) else Exit;

Reset(F, 64);

BlockRead(F, DoubleArray, 32, Transfered);

CloseFile(F);

ShowMessage('Считано '+IntToStr(Transfered)+' блок(-а,-ов)');

End;




Как видно из примера, размер блока установлен в процедуре Reset и кратен размеру элемента массива DoubleArray, в который считываются данные. В переменную Transfered возвращается число считанных блоков. Если размер файла меньше заданного в процедуре BlockRead числа блоков, ошибка не возникает, а в переменную Transfered передается число реально считанных блоков.

Обратите внимание, что для процедуры BlockRead организовывать цикл для чтения нескольких блоков нет необходимости.

Процедура


procedure BlockWrite(var f: File; var Buf; Count: Integer [; var AmtTransferred: Integer]);


используется аналогично.

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


type

TFileName = string;

TsearchRec = record

Time:Integer;

Size:Integer;

Attr:Integer;

Name: TFileName;


Эта запись обеспечивает хранение характеристик файла при удачном поиске. Дата и время создания файла хранятся в формате MS-DOS, поэтому для получения этих параметров в формате TDateTime необходимо использовать функцию


function FileDateToDateTime(FileDate: Integer): TDateTime;


Обратное преобразование выполняет функция


function DateTimeToFileDate(DateTime: TDateTime): Integer;


Свойство Attr может содержать комбинацию следующих значений:

faReadOnly – только для чтения;

faHidden – скрытый;

faSysFile – системный;

faVolumeID – метка;

faDirectory – каталог;

faArchive – архивный;

faAnyFile – любой.

Для определения параметров файла используется оператор AND :


If SearchRec.Attr AND faReadOnly) > 0 then ShowMessage('
файл только для чтения');


Непосредственно для поиска файлов используются функции FindFirst и FindNext .

Функция

function FindFirst(const Path: string; Attr: Integer; var F: TSearchRec): Integer;


находит первый файл, заданный полным маршрутом Path и параметрами Attr . Если заданный файл найден, функция возвращает 0, иначе – код ошибки. Параметры найденного файла возвращаются в записи F типа TSearchRec .

Функция

function FindNext(var F: TSearchRec): Integer;


используется для поиска файла. При этом используются параметры поиска, заданные последним вызовом функции FindFirst . В случае удачного поиска возвращается 0.

Для освобождения ресурсов, выделенных для выполнения поиска, применяется функция

procedure FindClose(var F: TSearchRec);


В качестве примера организации поиска файлов рассмотрим фрагмент исходного кода, в котором маршрут поиска файлов задается в однострочном текстовом редакторе DirEdit, а список найденных файлов передается в компонент TListBox.



procedure TForm1.FindBtnClick(Sender:TObject);

begin

ListBox.Items.Clear;

FindFirst(DirEdit.Text, faArchive + faHidden, SearchRec);

While FindNext(SearchRec) = 0 do

ListBox.Items.Add(SearchRec.Name);

FindClose(SearchRec);

end;


Напоследок, как и обещал в прошлом номере, немного о создании дополнительных форм. Итак, для создания дополнительной формы необходимо воспользоваться пунктом меню New Form ( File/New Form ) или кнопкой New Form на панели инструментов. Перед Вами появится новая форма проекта с названием Form2 . При запуске программы эта форма не будет активизирована. Для ее запуска необходимо воспользоваться процедурой Show из кода исходной программы. Список существующих форм и возможность активизация форм проекта доступны при нажатии на кнопку ViewForm на панели инструментов или при нажатии на пункт Forms… ( View/Forms… ). Таким образом, необязательно загромождать экран бесконечными формами проекта, когда на данный момент из них необходима только одна. Достаточно закрыть ненужные формы, а при необходимости активизировать их при помощи диалога ViewForm .

По материалам П. Дарахвелидзе и Е.Маркова.
Лаптенок Станислав
last@tut.by


(c) компьютерная газета


Компьютерная газета. Статья была опубликована в номере 49 за 2000 год в рубрике программирование :: delphi

©1997-2024 Компьютерная газета