Использование файловых переменных

Лабораторная работа №8

Работа с файлами

Задание:

Разработать приложение в среде Delphi, реализующие работу с файлами 4-мя способами: через файловые переменные, с использованием функций Windows API, c использование файловых потоков и с помощью файлов, отображаемых на память. Для всех способов доступа использовать один и тат же файл, одинаковый размер блока данных. Определить время, затрачиваемое программой на обработку файла (результат вывести в поле мемо). Ход обработки файла отобразить с помощью элемента Progress Bar. При нажатии на кнопку старт последовательно выполняются только отмеченные пользователем тесты.

Почему полученные результаты могут меняться? Предложите способ корректного измерения результатов и реализуйте его (подсказка: посмотреть функцию GetThreadTimes).

Отображение файлов на память:

function TfrmFileTest.CopyByMapping:Integer;

Type

TBuf=array[0..1024-1] of Byte;

PBuf=^TBuf;

Var

i,SrcSize:ULONG;

hSrc,hDst:THandle;

ptrIn,ptrOut:Pointer;

hMapFileIn,hMapFileOut:THandle;

BPI,BPO:PBuf;

Start,Finish:TDateTime;

Begin

if FileExists(DestinationFileName) then DeleteFile(DestinationFileName);

Start:=GetTime;

hSrc:=CreateFile(PAnsiChar(SourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);

hDst:=CreateFile(PAnsiChar(DestinationFileName),GENERIC_WRITE or GENERIC_READ,FILE_SHARE_WRITE,nil,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,0);

SrcSize:=GetFileSize(hSrc,nil);

prbStatus.Max:=SrcSize div 1024;

hMapFileIn:=CreateFileMapping(hSrc,nil,PAGE_READONLY,0,0,nil);

hMapFileOut:=CreateFileMapping(hDst,nil,PAGE_READWRITE,0,SrcSize,nil);

ptrIn:=MapViewOfFile(hMapFileIn,FILE_MAP_READ,0,0,0);

ptrOut:=MapViewOfFile(hMapFileOut,FILE_MAP_WRITE,0,0,SrcSize);

BPI:=PBuf(ptrIn);

BPO:=PBuf(ptrOut);

for i:=1 to SrcSize div 1024 do

Begin

BPO^:=BPI^;

Inc(BPO);

inc(BPI);

prbStatus.Position:=i;

Application.ProcessMessages;

end;

UnmapViewOfFile(ptrIn);

UnmapViewOfFile(ptrOut);

CloseHandle(hMapFileIn);

CloseHandle(hMapFileOut);

CloseHandle(hSrc);

CloseHandle(hDst);

Finish:=GetTime;

CopyByMapping:=MilliSecondsBetween(Start,Finish);

prbStatus.Position:=0;

end;

Использование Windows API:

function TfrmFileTest.CopyByWindowsAPI: integer;

Var

hSrc,hDst:THandle;

FileSize:ULONG;

Buf:array[0..1024-1]of Byte;

i:Integer;

Start,Finish:TDateTime;

Begin

if FileExists(DestinationFileName) then DeleteFile(DestinationFileName);

Start:=GetTime;

hSrc:=CreateFile(PAnsiChar(SourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);

hDst:=CreateFile(PAnsiChar(DestinationFileName),GENERIC_WRITE,FILE_SHARE_WRITE,nil,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,0);

FileSize:=GetFileSize(hSrc,nil);

prbStatus.Max:=FileSize div 1024;

for I:=1 to FileSize div 1024 do

Begin

// вставить операторы для чтения из файла источника и записи в файл приемник

prbStatus.Position:=i;

Application.ProcessMessages;

end;

CloseHandle(hSrc);

CloseHandle(hDst);

Finish:=GetTime;

CopyByWindowsAPI:=MilliSecondsBetween(Start,Finish);

prbStatus.Position:=0;

end;

Использование файловых потоков:

(Реализовать самостоятельно)

Использование файловых переменных:

(Реализовать самостоятельно)

Генерация тестового файла:

procedure TfrmFileTest.CreateTestFile;

var i,FileLen:integer;

F:TFileStream;

Buf:array[0..1023]of byte;

Begin

FileLen:= StrToInt(txtFileSize.Text);

prbStatus.Max:=FileLen*1024;

F:=TFileStream.Create(SourceFileName, fmCreate);

for i:=1 to FileLen*1024 do

begin

F.Write(Buf,1024);

prbStatus.Position:=i;

Application.ProcessMessages;

end;

F.Free;

end;

Среда Delphi предоставляет возможность выбрать один из четырех вариантов работы c файлами:

· использование традиционного набора функций работы с файлами, унаследованного от Turbo Pascal;

· использование функций ввода/вывода из Windows API;

· использование потоков (TStream и его потомки);

· использование отображаемых файлов.

Использование файловых переменных

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

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

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

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

var UntypedFile: file;

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

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

var ByteFile: file of byte;

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

type Country = record

Name: String;

Capital: String;

Population: Longlnt;

Square: Longlnt;

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 OpenDlg.Execute then

AssignFiie(F, OpenDlg.FileName)

else Exit;

Reset(F);

while Not EOF(F) do

Begin

Readln(F, S);

Memo.Lines.Add(S);

end;

CloseFile(F);

end;

Если в диалоге открытия файла OpenDlg был выбран файл, то его имя связывается с файловой переменной F при помощи процедуры AssignFile. В качестве имени файла рекомендуется всегда передавать полное имя файла (включая его маршрут). Как раз в таком виде возвращают результат выбора файла диалоги работы с файлами TOpenDialog, TOpenPictureDiaiog. Затем при помощи процедуры Reset этот файл открывается для чтения и записи.

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

После завершения чтения файл закрывается.

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

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

Открытие файла может осуществляться тремя процедурами — в зависимости от типа его дальнейшего использования.

procedure Reset(var F: File [; RecSize: Word ]); открывает существующий файл для чтения и записи, текущая позиция устанавливается на первой строке файла.
procedure Append(var F: Text); открывает файл для записи информации после его последней строки, текущая позиция устанавливается на конец файла.
procedure Rewrite(var F: File [; RecSize: Word ]); создает новый файл и открывает его, текущая позиция устанавливается в начало файла. Если файл с таким именем уже существует, то он перезаписывается. Переменная RecSize используется только при работе с нетипизированными файлами и определяет размер одной записи для операции передачи данных. Если этот параметр опущен, то по умолчанию RecSize равно 128 байт.

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

procedure Read([var F: Text;] V1 [, V2,...,Vn]); procedure Read(F, V1 [, V2,...,Vn]); При одном вызове процедуры можно читать данные в произвольное число переменных. Естественно, что тип переменных должен совпадать с типом файла. При чтении в очередную переменную читается ровно столько байтов из файла, сколько занимает тип данных. В следующую переменную читается столько же байтов, расположенных следом. После выполнения процедуры текущая позиция устанавливается на первом непрочитанном байте. Аналогично работают несколько процедур Read для одной переменной, выполненных подряд.
procedure Readln([ var F: Text; ] VI [, V2,...,Vn ]); считывает одну строку текстового файла и устанавливает текущую позицию на следующей строке. Если использовать процедуру без переменных v1..vn, то она просто передвигает текущую позицию на очередную строку файла.
procedure Write([var F: Text; ] PI [, P2,..., Pn]); procedure Writeln([ var F: Text; ] PI [, P2,...,Pn ]); Параметры P1, P2,..., Pn могут быть одним из целых или вещественных типов, одним из строковых типов или логическим типом. Но у них есть возможность дополнительного форматирования при выводе.

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

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

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

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

Процедура

procedure BlockRead(var F:File; var Buf; Count:Integer [; var AmtTransferred:Integer]); выполняет запись блока из файла в буфер. Параметр F ссылается на нетипизированную файловую переменную, связанную с нужным файлом.

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

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

procedure TForm2.Button1Click(Sender: TObject);

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, Transferee!);

CloseFile(F);

ShowMessage('Ñ÷èòàíî '+IntToStr(Transfered)+' áëîêîâ');

end;

end;

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

Процедура

procedure BlockWrite(var f: File; var Buf; Count: Integer [; var AmtTransferred: Integer]); используется аналогично.

Таблица 1. Процедуры и функции для работы с файлом

Объявление Описание
ChangeFileExt Функция позволяет изменить расширение файла. При этом сам файл не переименовывается.
ChDir Процедура изменяет текущий каталог на другой, путь к которому задается параметром
CloseFile Вызов процедуры разрывает связь между файловой переменной и файлом на диске.
DeleteFile Функция производит удаление файла с диска и возвращает значение False, если файл удалить не удалось или файл не существует
ExtractFileExt Функция возвращает расширение файла
ExtractFileName Извлекает имя и расширение файла, содержащегося в параметре FileName
ExtractFilePath Функция возвращает полный путь к файлу
Erase Удаляет файл, связанный с файловой переменной
FileSearch Данная процедура производит поиск в каталогах DirList файла Name. Если в процессе выполнения FileSearch обнаруживается искомое имя файла, то функция возвращает в строке типа string полный путь к найденному файлу. Если файл не найден, то возвращается пустая строка
FileSetAttr Присваивает файлу с именем FileName атрибуты Attr. Функция возвращает 0, если присвоение атрибутов прошло успешно. В противном случае возвращается код ошибки
FilePos Возвращает текущую позицию файла. Функция используется для нетекстовых файлов. Перед вызовом FilePos файл должен быть открыт
MkDir Процедура создает новый каталог
Rename Процедура изменяет имя файла, связанного с файловой переменной F. Переменная NewName является строкой типа string или PChar (если включена поддержка расширенного синтаксиса)
RmDir Процедура удаляет пустой каталог, путь к которому задается в строке S. Если указанный каталог не существует или он не пустой, то возникает сообщение об ошибке ввода/вывода

Потоки

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


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: