File — Ключевое слово Delphi


File — Ключевое слово Delphi

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

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

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

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

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

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

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

v ar ByteFile: file of byte;

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


type Country = record

var CountryFile: file of Country;

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

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

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

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

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

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

While Not EOF(F) do

Если в диалоге 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 и т.д. Фрагмент исходного кода блочного чтения из такого файла выглядит так:

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

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

BlockRead(F, DoubleArray, 32, Transfered);

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

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

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


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

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

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


function FileDateToDateTime(FileDate: Integer): TDateTime;

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


function DateTimeToFileDate(DateTime: TDateTime): Integer;

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

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

Для определения параметров файла используется оператор 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.

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

While FindNext(SearchRec) = 0 do

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

Работа с текстовыми файлами в среде программирования Делфи.

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

Скачать:

Вложение Размер
rabota_s_tekstovymi_faylami_v_delfi.docx 48.59 КБ

Предварительный просмотр:

Работа с текстовыми файлами в среде программирования Делфи.

Текстовый файл отличается тем что он разбит на разные по длине строки, отделенные символами #13#10. Есть 2 основных метода работы с текстовыми файлами — старый паскалевский способ и через файловые потоки. У обоих есть преимущества и недостатки. Через потоки способ проще поэтому начнем с него.

Итак у всех потомков класса TStrings (TStringList, memo.Lines и т.п. ) есть методы записи и чтения в файл — SaveToFile, LoadFromFile. Преимущество — простота использования и довольно высокая скорость, недостаток — читать и писать файл можно только целиком.

1. Загрузка текста из файла в Memo:

2. Сохранение в файл:

3. А вот так можно прочитать весь файл в строку:

function ReadFromFile(FileName: string ): string ;
begin
with TStringList.Create do
try
LoadFromFile(FileName);
result := text;
finally
Free;
end ;
end ;

Паскалевский метод доступа

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

Итак, для доступа к текстовым файлам используется переменная типа TextFile . До сих пор не совсем понимаю что это такое физически — что-то типа «внутреннего» паскалевского Handle на файл.

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

1) Определяем файловую переменную:

2) Ассоциируем ее:

3) Теперь надо этот файл открыть, есть 3 варианта:

  • файла нет или он должен быть перезаписан, открытие для записи: Rewrite(f)
  • файл есть и его надо открыть для чтения (с первой строки): Reset(f)
  • файл есть и его надо открыть для дописования строк в конец: Append(f)

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

  • Перезаписать весть файл
  • Читать с первой строки
  • Дописать что-то в конец
  • Читать и писать файл целиком (см. выше работу через TStrings)
  • В конце работы открытый файл нужно закрыть:

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

Чтение предварительно открытого файла:

ReadLn(f, s) — будет прочитанна текущая строка и позиция чтения переведена на следующую позицию.

А как прочитать весь файл?

Хорошо, а если файл очень большого размера, есть ли способ поставить какой-нибудь ProgressBar или Gauge чтобы показывал сколько считанно? Есть, но не совсем прямой — не забыли, сколько строк в файле заранее мы не знаем, узнать можно только прочитав его весь, но показометер мы все-таки сделаем:

var
Canceled: Boolean;

function GetFileSize(FIleName: string ): integer;
var
f: file of Byte;
begin
try
AssignFile(f, FileName);
Reset(f);
result := filesize(F);
CloseFile(f);
except
result := -1;
end ;
end ;

procedure ReadMyFile;
var
i, j: integer;
begin
ProgressBar1.Max := GetFileSize(‘c:\MyFile.txt’);
ProgressBar1.position := 0;
AssignFile(f, ‘c:\MyFile.txt’);
Canceled := False;
reset(f);
i := 0;
j := 0;
while not eof(f) do
begin
inc(j);
readln(f, s);
i := i + length(s) + 2;
if (j mod 1000) = 0 then
begin
ProgressBar1.position := i;
Application.ProcessMessages;
if canceled then break;
end ;
<здесь мы что-то делаем с прочитанной строкой>
end ;
CloseFile(f);
end ;

Теперь комментарии к коду.

Функию GetFileSize я рсссмотрю после, она немного по другому подходит к чтению файла (кстати я знаю еще по крайней мере 3 способа ее реализации, поэтому не нужно указывать что это можно сделать легче, быстрее или просто по другому — просто давайте разберем это позже).

Переменная i — все время указывает на количество байт которое мы считали — мы определяем длину каждой строки и прибавляем 2 (символы конца строки). Зная длину файла в байтах и сколько байт прочитано можно оценить и прогресс, но eсли ставить изменение прогресса после каждой строки, то это очень сильно тормознет процесс. Поэтому вводим переменную j и обновляем прогресс например 1 раз на 1000 прочитанных строк.

Переменная Canceled — глобальная переменная. Поставьте на форму кнопку, в обработчике нажатия поставьте Canceled:=True; и нажатие кнопки прервет чтение файла.

Теперь как писать в текстовый файл:

Запись целой строки:

Запись кусочка строки(те следующая операция записи будет произведена в ту же строку):

Если переменная s содержит больше 255 символов (т.е. является длинной строкой), то таким способом ни фига не запишится, в файл вместо строки попадут 4 байта указателя на нее. Надо делать так:

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

Объявляем файл байтов:

var
f: file of byte;
b: Byte;

Ассоциируем файловую переменную с физическим файлом:

Теперь мы можем либо перезаписать/создать файл:

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

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

Теперь функции работы с файлом:

read(f,b); — прочитать 1 байт

write(f,b); — записать 1 байт

seek(f,100); — поставить текущее положение считывания/записи на сотый байт

Size(f); — прочитать количество байт в файле.

Eof(f); — узнать не являетсмя ли байт последним

Все эти функции не работают с файлами большими 2 Gb.

После работы файл надо закрыть:

Приведенные выше механизмы будут работать с любым файлом, так как любой файл можно считать файлом байтов. Теперь где это можно использовать? В принципе везде, но в подавляющем большинстве случаев это будет очень неудобно, ведь скорость считывания при чтении по байтам будет на порядки более низкой чем другими способами. Однако в некоторых случаях этот способ может быть очень полезен. Например в программе вам надо заменить 100й байт файла на другой, или прочитать 100й байт файла, например во всяких читерских программах, при взломе и т.п. Здесь такой доступ будет весьма удобен. Гораздо более интересным представляется дальнейшее развитие технологии типизированных файлов (их еще лет 15 назад называли «Файлы прямого доступа»). Представим себе, что файл состоит не из байт а из более сложных структур. Например мы имеем некоторую информацию в виде:

type
MyRec = record
Name: string [100];
Age: byte;
Membership: Boolean;
Accounts: array [1..10] of integer;
end ;

Обратите внимание, что все элементы записи точно типизированны, нет ни длинных строк, ни открытых массивов, ни объектов, другими словами, заранее точно известно сколько именно байт будет занимать переменная этого типа. Объявим переменную этого типа:

var
MyVar: MyRec;

и файл этого типа:

var
f: File of MyRec;

Теперь мы можем читать и писать сразу целую структуру, абсолютно так же как и если бы это был один байт:

AssignFile(f, ‘c:\MyFile.rec’);
Rewrite(f);
MyVar.Name := ‘Vitaly’;
MyVar.Age := 33;
MyVar.Membership := True;
MyVar.Accounts[1] := 12345;
MyVar.Accounts[2] := 34985;
Write(f, MyVar);
Closefile(f);

Все остальные функции приведенные в предыдущей статье будут работать так же, только одно отличие — Seek и Size оперируют не с количеством байт, а с количеством записей.

Идем дальше. Есть такое понятие как нетипизированный файл. Это такой файл который содержит разнородные элементы. Например файл EXE — вначале он имеет заголовок, затем двоичный код, в конце какие-то ресурсы. Все части файла имеют разную длину и разную структуру. Тут уже обратится к произвольному элементу сложно, обычно надо вначале узнать где этот элемент находится, подчас это записано в предыдущем куске информации. Работа с такими файлами достаточно сложна и требует вручную разработки алгоритмов его чтения, но в связи гибкостью структуры и компактностью такие файлы составляют большинство. Для работы с нетипизированными файлами используют процедуры BlockRead и BlockWrite, которые позволяют читать/писать произвольное количество байт. Привожу пример пользования этими функциями из справки по Дельфи:

var
FromF, ToF: file ;
NumRead, NumWritten: Integer;
Buf: array [1..2048] of Char;
begin
if OpenDialog1.Execute then <показываем диалог открытия>
begin
AssignFile(FromF, OpenDialog1.FileName);
Reset(FromF, 1); < Record size = 1 >
if SaveDialog1.Execute then <показываем диалог сохранения>
begin
AssignFile(ToF, SaveDialog1.FileName);
Rewrite(ToF, 1); <запись размером 1>
Canvas.TextOut(10,10, ‘Copying ‘ +IntToStr(FileSize(FromF))+ ‘ bytes. ‘ );
repeat
BlockRead(FromF, Buf, SizeOf(Buf), NumRead);
BlockWrite(ToF, Buf, NumRead, NumWritten);
until (NumRead = 0) or (NumWritten <> NumRead);

CloseFile(FromF);
CloseFile(ToF);
end ;
end ;
end ;

Этот код копирует из одного файла в другой. Замечания по поводу этого метода работы с файлами — плюсы — очень высокая скорость, особенно если размер буффера увеличить до 64kb-512kb, что позволит считывать файл достаточно большими кусками, чтобы обеспечить отсутствие простоев винчестера, к тому же обеспечивается очень высокая гибкость в работе. Минусы — сложность разработки, необходимость вручную писать все детали механизма чтения/записи и интерпретации данных.

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

Erase(f) — удаляет файл

FilePos(f) — возвращает текущую позицию чтения/записи в файл

Flush(f) — сбрасывает кэшированные файловые операции на диск

Rename(f, ‘MyNewFileName.txt’ ) — переименование файлов

Truncate(f) — файл обрезается до текущей позиции чтения/записи

Теперь разберем возможности работы потомка TStream — TFileStream — файловый поток. Этот класс был специально введен для работы с файлами. Для работы с файловым потоком Вам надо записать в Uses модули classes, Sysutils (classes — включает в себя собственно определение класса, Sysutils — некоторые константы необходимые для работы).

Вот пример записи/перезаписи файла:

procedure WriteFileUsingStream(s, FileName: string );
begin
with TFileStream.create(FileName, fmCreate or fmOpenWrite) do
try
write(pointer(s)^, length(s));
finally
free;
end ;
end ;

Теперь небольшой разбор:

TFileStream.create — конструктор класса, его вызов требует указания имени файла и опций его открытия, следующие опции определены:

fmCreate = $FFFF;
fmOpenRead = $0000;
fmOpenWrite = $0001;
fmOpenReadWrite = $0002;
fmShareCompat = $0000;
fmShareExclusive = $0010;
fmShareDenyWrite = $0020;
fmShareDenyRead = $0030;
fmShareDenyNone = $0040;

Теперь метод Write — этим методом в файл пишется любая информация из буфера любого типа, Вам надо указать только буффер и количество записываемых байтов. В данном случае используется переменная типа String в качестве буффера, но так как для длинных строк она представляет собой лишь указатель, то конструкция «pointer(s)^» заставляет обращаться именно к ее содержимому.

А вот этот код демонстрирует чтение файла с использованием файлового потока:

var
p: PChar;
begin
GetMem(p, 255);
with TFileStream.create( ‘c:\myText.txt’ , fmOpenReadWrite) do
try
Seek(10, soFromBeginning);
read(p^, 254);
finally
free;
end ;
showmessage(p);
FreeMem(p);
end ;

И пояснения к коду:

Никаких проверок длину файла и его наличие здесь не делается — это демонстрационный код, а не готовая процедура чтения.

Файл мы считываем в буффер типа PChar (с тем же успехом можно использовать массив или любой другой контейнер). Для тех кто не помнит — процедуры GetMem(p, 255) и FreeMem(p) — распределение памяти для строки и освобождение памяти.

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

soFromBeginning — от начала файла

soFromCurrent — от текущей позиции считывания

soFromEnd — от конца файла (в этом случае номер байта должен быть отрицательным или равным нулю)

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

Заканчивая о файловых потоках хочу упомянуть о методе CopyFrom который позволяет перекачивать информацию из одного потока в другой и о свойствах:

Size — размер файла

Position — текущая позиция чтения/записи потока

Работа с файловыми потоками весьма быстра, этот класс, являсь классом VCL, в то же время базируется на низкоуровневых функциях Windows, что обеспечивает очень высокую скорость работы и стабильность операций. К тому же многие компоненты и классы VCL поддерживаю прямое чтение и запись с файловыми потоками, что занчительно упрощает работу — например TStringList, TBlobField, TMemoField и другие.

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

Работа через Handle

Еще один способ работы с файлами — это открытие Handle на файл и работу через него. Тут есть 2 варианта — можно использовать функции Дельфи или использовать WinAPI напрямую.

При использовании функций Дельфи можно применять следующие функции:

FileOpen(FileName, fmOpenWrite or fmShareDenyNone) — функция открывает файл и возвращает целое цисло — Handle на файл. Параметры функции — имя файла и тип доступа (все типы доступа я перечислил ранее). Если файл успешно открыт то Handle должен быть положительным цислом, отрицательное число — это код ошибки.

Во всех остальных функциях используется именно значение Handle, возвращаемое этой функцией.

FileClose(Handle: Integer) — закрывает файл

FileRead(Handle: Integer; var Buffer; Count: Integer): Integer;

FileWrite(Handle: Integer; const Buffer; Count: Integer): Integer;

Эти функции для чтения/записи файла, где Buffer любая переменная достаточного размера для чтения/записи куска информации (обычно типа PChar или массив), Count-количество байт, которое Вы желаете записать/прочитать. Функции возвращают количество байт которые реально были прочитанны или записаны.

Этот тип доступа к файлам применяется весьма редко. Дело в том что он практически дублирует соответствующие функции WinAPI и к тому же обычно работает несколько медленнее, чем например потоки. И все же использование функций FileOpen и FileClose не лишено привлекательности. Наряду с тем что эти функции намного легче в использовании соответствующих функций WinAPI (можете сравнить — FileOpen имеет 2 параметра, cooтветствующая функция WinAPI — CreateFile имеет 7 параметров, большая часть из которых реально требуется лишь в ограниченном числе случаев) этот путь доступа открывает возможность прямого использования всех функций WinAPI про работе с файлами, которые требуют Handle на открытый файл.

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

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

ChDir(NewCurrentPath: string); — изменяет текущий каталог (в среде Windows сие конечно не так актуально как в ДОС, но все же), прочитать же текущий каталог можно функцией GetCurrentDir, а текущий каталог для определенного драйва — GetDir.

CreateDir(const Dir: string): Boolean; — создает каталог. При этом предыдущий уровень должен присутствовать. Если вы хотите сразу создать всю вложенность каталогов используйте функцию ForceDirectories(Dir: string): Boolean; Обе функции возвращают True если каталог создан

DiskFree(Drive: Byte): Int64; — дает свободное место на диске. Параметер — номер диска 0 = текущий, 1 = A, 2 = B, и так далее

DiskSize(Drive: Byte): Int64; — размер винта. Обратите внимание на то что для результата этой и предыдущей функций абсолютно необходимо использовать переменную типа Int64, иначе макимум того что вы сможете прочитать правильно будет ограничен 2Gb

FileExists(const FileName: string) — применяется для проверки наличия файла

FileGetAttr(const FileName: string): Integer;

FileSetAttr(const FileName: string; Attr: Integer): Integer; — функции для работы с атрибутами файлов. Вот список возможных атрибутов:

faReadOnly $00000001 Read-only files
faHidden $00000002 Hidden files
faSysFile $00000004 System files
faVolumeID $00000008 Volume ID files
faDirectory $00000010 Directory files
faArchive $00000020 Archive files
faAnyFile $0000003F Any file

(Естественно не все атрибуты применимы во всех случаях)

RemoveDir(const Dir: string): Boolean; — удаляет папку(пустую)

DeleteFile(const FileName: string): Boolean; — удаляет файл

RenameFile(const OldName, NewName: string) — переименовывает файл

Информация о файле

Привожу пример функции которая собирает довольно большое количество информации о выбранном файле:

type
TFileInfo = record
Exists: boolean; //true если файл найден
Name: string ; //имя файла с расширением
ShortName: string ; //DOS 8.3 имя файла
NameNoExt: string ; //имя файла без расширения
Extension: string ; //расширение файла
AssociatedFile: string ; //программа с которой ассоциирован файл
Path: string ; // путь к файлу
ShortPath: string ; // DOS 8.3 путь файла
Drive: string ; // дисковод на котором находится файл
CreateDate: TDateTime; //время когда файл создан
Size: Int64; // размер файла (работает для файлов и больше 2Gb)
Attributes: record //наличие/отсутствие системных атрибутов
ReadOnly: boolean;
Hidden: boolean;
System: boolean;
Archive: boolean;
end ;
ModifyDate: TDateTime; // время последнего изменения файла
LastAccessDate: TDateTime; // дата последнего открытия файла
end ;

function ReadFileInfo(FileName: string ): TFileInfo;
var
ts: TSearchRec;

function FileTime2DateTime(FT: _FileTime): TDateTime;
var
FileTime: _SystemTime;
begin
FileTimeToLocalFileTime(FT, FT);
FileTimeToSystemTime(FT, FileTime);
Result := EncodeDate(FileTime.wYear, FileTime.wMonth, FileTime.wDay)+
EncodeTime(FileTime.wHour, FileTime.wMinute, FileTime.wSecond,
FileTime.wMilliseconds);
end ;

function AssociatedFile(FileExt: string ): string ;
var
key: string ;
begin
with TRegistry.create do
try
RootKey := HKEY_CLASSES_ROOT;
OpenKey(FileExt, false);
Key := ReadString( » );
CloseKey;
OpenKey(key + ‘\Shell\open\command’ , false);
result := ReadString( » );
Closekey;
finally
free;
end
end ;

begin
Result.Name := ExtractFileName(FileName);
Result.Extension := ExtractFileExt(FileName);
Result.NameNoExt := Copy(Result.Name, 1, length(Result.Name) —
length(Result.Extension));
Result.Path := ExtractFilePath(FileName);
Result.Drive := ExtractFileDrive(FileName);
Result.ShortPath := ExtractShortPathName(ExtractFilePath(FileName));
if lowercase(Result.Extension) <> ‘.exe’ then
Result.AssociatedFile := AssociatedFile(Result.Extension);
if FindFirst(FileName, faAnyFile, ts) = 0 then
begin
Result.Exists := true;
Result.CreateDate := FileDateToDateTime(ts.Time);
Result.Size := ts.FindData.nFileSizeHigh * 4294967296 +
ts.FindData.nFileSizeLow;
Result.Attributes.ReadOnly := (faReadOnly and ts.Attr) > 0;
Result.Attributes.H > 0;
Result.Attributes.System := (faSysFile and ts.Attr) > 0;
Result.Attributes.Archive := (faArchive and ts.Attr) > 0;
Result.ModifyDate := FileTime2DateTime(ts.FindData.ftLastWriteTime);
Result.LastAccessDate := FileTime2DateTime(ts.FindData.ftLastAccessTime);
Result.ShortName := ts.FindData.cAlternateFileName;
Findclose(ts);
end
else
Result.Exists := false;
end ;

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

Теперь поговорим о поиске файлов. Для этой цели могут использоваться процедуры FindFirst, FindNext, FindClose, при участии переменной типа TSearchRec которая хранит информацию о текущем статусе поиска и характеристики последнего найденного файла.

Пример иллюстрирующий поиск всех файлов и каталогов в определенном каталоге:

var
SearchRec: TSearchRec;
.
if FindFirst( ‘c:\Windows\*.*’ , faAnyFile, SearchRec) = 0 then
repeat
<Вот здесь мы можем делать с найденным файлом что угодно
SearchRec.name — имя файла
ExpandFileName(SearchRec.name) — имя файла с полным путем>
until
FindNext(SearchRec) <> 0;

Примечания по приведенному коду:

Первыми в список могут попадать файлы с именами «.» и «..» — это ДОСовские имена для переходов на «родительский уровень», иногда нужна обработка для их игнорирования.

FindFirst в качестве первого параметра принимает шаблон для поиска, так как он был принят для ДОС. Если шаблон не включает путь то файлы будут искаться в текущем каталоге.

FindFirst требует задания атрибута для файла — здесь мы искали все файлы, если надо какие-то определенные (например только скрытые, или только каталоги) то надо это указать, список всех атрибутов я уже приводил выше.

SearchRec переменная связывает во едино FindFirst и FindNext, но требует ресурсов для своей работы, поэтому желательно ее освободить после поиска процедурой FindClose(SearchRec) — на самом деле утечки памяти небольшие, но если программа работает в цикле и долгое время пожирание ресурсов будет значительным.

FindFirst/FindNext — работают не открывая файлы, поэтому они корректно находят даже Swap файлы Windows.

Файлы Delphi

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

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

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

Объявления файлов Delphi

Как и в случае любой другой структуры данных (переменной, либо массива и т.д.), файл нужно объявить в разделе описания переменных var. Если вам нужно объявить файл в разделе var, то обязательно укажите тип элементов данного файла. Общий вид инструкции объявления файла представлен следующим образом:

  • name обозначает наименование (название) файла,
  • type – тип элементов указанного файла,
  • file – специальное ключевое слово, обозначающее файл.

Файлы Delphi. Примеры

drobi: file of real; // файл, включающий вещественные числа

symbol: file of char; // файл, содержащий символы

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

  • name обозначает наименование (название) текстового файла;
  • Textfile выступает в качестве обозначения типа, означающего, что name есть файловая переменная, которая представляет текстовый файл.

File — Ключевое слово Delphi

Файлы и устройства ввода/вывода

Большинство приложений создаются для того, чтобы обрабатывать данные — это прописная истина. С развитием программных технологий мы получаем возможность получать и посылать все более крупные и сложные массивы данных; однако до сих пор 90% из них хранятся в файлах.

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

Основные принципы и структура файловой системы мало изменились со времен MS DOS. Новые файловые системы (FAT32, NTFS) и появившаяся в Windows 2000 служба Active Directory не изменяют главного — понятия файла и способов обращения к нему.

Среда Delphi дает вам возможность выбрать один из четырех вариантов работы:

  • использование традиционного набора функций работы с файлами, унаследованного от Turbo Pascal;
  • использование функций ввода/вывода Windows API;
  • использование потоков (TStream и его потомки);
  • использование отображаемых файлов.

В этой главе мы изучим все основные способы работы с файлами в приложениях Delphi на конкретных примерах создания программного кода.

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

Зачастую современный программный код Delphi для чтения данных из файла удивительно похож на аналогичный, написанный, к примеру, в Turbo Pascal 4.0. Это возможно потому, что программисты Borland сохранили неизменным «старый добрый» набор файловых функций, работающих через файловые переменные.

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

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

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

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

var UntypedFile: file;

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

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

var ByteFile: file of byte;

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

type Country = record

var CountryFile: file of Country;

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

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

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

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

Обратите внимание: по сравнению с Turbo Pascal изменились названия только двух функций: Assign стала AssignFile, a close превратилась в closeFilc.

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

if OpenDIg.Execute then AssignFile(F, OpenDlg.FileName) else Exits; Reset(F);

while Not EOF(F) do

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

В цикле осуществляется чтение из файла текстовых строк, и запись их в компонент тмето. Процедура 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 И Readin.

Процедура Read имеет различное объявление для текстовых и других типизированных файлов:

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

procedure Read(F, VI [, -V2. Vn]); — для других типизированных

При одном вызове процедуры можно читать данные в произвольное число переменных. Естественно, что тип переменных должен совпадать с типом

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

procedure Readln([var F: Text;] VI [,V2, . Vn ]);

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

Процедуры для записи в файл write и writein описываются аналогично:

procedure Write([var F: Text;] P1 [,P2. Pn] );

procedure Writeln([var F: Text;] P1 [,P2. Pn ] );

Параметры p1, p2, . , pn могут быть одним из целых, вещественных, строковых типов или логическим типом. Но у них есть возможность дополнительного форматирования при выводе. Каждый параметр записи может иметь форму:

Pn [: MinWidth [: DecPlaces ] ] где pn — выводимая переменная или выражение;

MinWidth — минимальная ширина поля в символах, которая должна быть больше 0;

DecPlaces — содержит количество десятичных символов после запятой при отображении вещественных чисел с фиксированной точкой.

Обратите внимание, что для текстовых файлов в функциях Read и write файловая переменная F может быть опущена. В этом случае чтение и запись осуществляются в стандартные файлы ввода/вывода. Когда программа компилируется как консольное приложение (флаг ($apptype console)), Delphi автоматически связывает входной и выходной файлы с окном консоли.

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

обеспечивает смещение текущей позиции на N элементов. Размер одного элемента в байтах зависит от типа данных файла (типизированной переменной).

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

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

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

выполняет запись блока из файла в буфер. Параметр f ссылается на не типизированную файловую переменную, связанную с нужным файлом.

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

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

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

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

BlockRead(F, DoubleArray, 32, Transfered);

ShowMessage(‘Считано’ + IntToStr(Transfered) + ‘блоков’);

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

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

Оставшиеся функции ввода/вывода, работающие с файловыми переменными, подробно описаны в табл. 6.1.

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

function ChangeFileExt(const FileName, Extension: string): string;

Функция позволяет изменить расширение файла, при этом сам файл не переименовывается

Процедура изменяет текущий каталог на другой, путь к которому описан в строке s

procedure CloseFile(var F) ;

Вызов процедуры разрывает связь между файловой переменной и файлом на диске

Имя этой процедуры изменено из-за конфликта имен в Delphi (в Borland Pascal используется процедура Close)

function DeleteFile(const FileName: string): Boolean;

Функция производит удаление файла FileName с диска и возвращает значение False, если файл удалить не удалось или файл не существует

function ExtractFile-Ext(const FileName: string): string;

Функция возвращает расширение файла

function ExtractFileName(const FileName:

Извлекает имя и расширение файла, переданного в параметре FileName

function ExtractFilePathfconst FileName: string): string;

Функция возвращает полный путь к файлу

procedure Erase(var F) ;

Удаляет файл, связанный с файловой переменной F

function FileSearch(const Name, DirL ist: string): string;

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

Function FileSetAttr(const FileName: string; Attr: Integer): Integer;

Присваивает файлу с именем FileName атрибуты Attr. Функция возвращает 0, если присвоение атрибутов прошло успешно. В противном случае возвращается код ошибки

function FilePos(var F): Longint;

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

function FileSize(var F): Integer;

FileSize возвращает размер файла в байтах или количество записей в файле, содержащем записи.

Перед вызовом данной функции файл должен быть открыт.

Для текстовых файлов функция FileSize не используется.

procedure Flush(var F: Text) ;

Процедура очищает буфер текстового файла открытого для записи. F — файловая переменная.

Когда текстовый файл открыт для записи с использованием функций Rewrite или Append, Flush очищает выходной буфер, связанный с файлом.

После выполнения данной пооцедуры все символы, которые направлены для записи в файл, будут гарантированно записаны в нем.

Byte; var S: strings-

Возвращает число, соответствующее диску, на ко

тором содержится текущий каталог s

D может принимать одно из следующих значений: О-по умолчанию (текущий), 1 — А, 2- В, 3-Си так далее.

Процедура не генерирует код ошибки. Если имя диска Х в D оказывается ошибочным, то в строке S возвращается значение Х:\ как если бы текущая директория была на этом ошибочно указанном диске

function IOResult: Integer; procedure MkDir(S: string);

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

Процедура создает новый каталог, который описывается в строке S

procedure Rename(var F; NewName: string);

Процедура изменяет имя файла, связанного с файловой переменной F. Переменная NewName является строкой типа string или типа pchar (если включена поддержка расширенного синтаксиса)

procedurе RmDir (S: string) ;

Процедура удаляет пустой каталог, путь к которому задается в строке S . Если указанный каталог не существует или он не пустой, то возникает сообщение об ошибке ввода/вывода

procedure Seek (var F; N: Longint) ;

Перемещает текущую позицию курсора на N позиций. Данная процедура используется только для открытых типизированных или не типизированных файлов для проведения чтения-записи с нужной позиции файла.

Началу файла соответствует нулевой номер позиции.

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

function SeekEof[(var F: Text)]: Boolean;

Возвращает значение True, если указатель текущей позиции находится на символе конца файла.

SeekEof может быть использована только с открытым текстовым файлом

function SeekEoln[(var F: Text)]: Boolean;

Возвращает значение True, если указатель текущей позиции находится на символе конца строки.

SeekEoln может быть использована только с открытым текстовым файлом

procedure SetTextBuf(var F: Text; var Buf [ ; Size: Integer]);

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

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

SetTextBuf позволяет помещать в текстовый файл F информацию об операциях ввода-вывода вместо ее размещения в буфере, size указывает размер буфера в байтах. Если этот параметр опускается, то полагается размер, равный SizeOf (Buf). Новый буфер действует до тех пор, пока F не будет связана с новым файлом процедурой AssignFile.

procedure Truncate (var F);

Удаляет все позиции, следующие после текущей позиции в файле. А текущая позиция становится концом файла.

С переменной F может быть связан файл любого типа, за исключением текстового

Ввод/вывод с использованием Windows API

Для тех, кто переходит на Delphi не с прежних версий Turbo Pascal, а с других языков программирования или начинает освоение с нуля, более привычными будут стандартные функции работы с файлами Windows. Тем более что возможности ввода/вывода в них расширены. Каждый файл в этом наборе функций .описывается не переменной, а дескриптором (Handle) — 32-разрядной величиной, которая идентифицирует файл в операционной системе.

В Win32 файл открывается при помощи функции, имеющей обманчивое название

function CreateFileflpFileName: PChar;

dwCreationDistribution, dwFlagsAndAttributes: DWORD;

hTemplateFile: THandle): THandle;

Хоть ее название и начинается с create, но она позволяет не только создавать, но и открывать уже существующие файлы.

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

Таблица 6.2. Параметры функции createFile

Имя открываемого объекта. Может представлять собой традиционную строку с путем и именем файла, UNC (для открытия объектов в сети, имя порта, драйвера или устройства)

Способ доступа к объекту. Может быть равен:

GENERIC READ — для чтения;

GENERIC WRITE — для записи.

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

Режим совместного использования файла:

0 — совместный доступ запрещен;

FILE_ SHARE READ -для чтения;

FILE_ SHARE_ WRITE — для записи.

Комбинация FILE_ SHARE READ и

File_ share_ write -для полного совместного доступа

Атрибуты защиты файла. В Windows 95/98 не используются (должны быть равны nil). В Windows NT/2000 этот параметр, равный nil, дает объекту атрибуты по умолчанию

DwCreationDistributio n: DWORD ;

Способ открытия файла:

Create_ new — создается новый файл; если таковой уже существует, функция возвращает ошибку

Create_ always — создается новый файл; если таковой уже существует, он перезаписывается;

Open_ existing — открывает существующий файл, если таковой не найден, функция возвращает ошибку;

Open_ always — открывает существующий файл, если таковой не найден, он создается

Набор атрибутов (скрытый, системный, сжатый) и флагов для открытия объекта. Подробное описание смотри в документации по Win32

Файл-шаблон, атрибуты которого используются для открытия. В Windows 95/98 не используется и должен быть равен 0

Функция creafeFile возвращает дескриптор открытого объекта ввода/вывода. Если открытие невозможно из-за ошибок, возвращается код invalid_handle_value, а расширенный код ошибки можно узнать, вызвав функцию GetLastError.

Закрывается файл в Win32 функцией closeHandle (не closeFile, a closeHandie!) Правда, «легко» запомнить? Что поделать, так их назвали разработчики Win32).

Приведем несколько приемов из большого разнообразия использования createFile. Часто программисты хотят иметь возможность организовать посекторный доступ к физическим устройствам хранения — например, к дискете. Сделать это не так уж сложно, но при этом методы для Windows 95 и NT различаются. В NT придется открывать устройство (‘\\.\ A:’), а в Windows 95 — специальный драйвер доступа (обозначается ‘\\. \vwin32’). И то и другое делается функцией CreateFile.

Листинг 6.1 Чтение сектора с дискеты при помощи функции CreateFile

TDIOCRegs = packed record rEBX,rEDX,rECX,rEAX,rEDI,rESI,rFlags : DWORD;

const VWIN32_DIOC_DOS_IOCTL = 1;

VWIN32_DIOC_DOS_INT13 =4; // Performs Interrupt 13h commands.

function ReadSector(Head, Track, Sector: Integer; buffer: pointed; Floppy: char):Boolean;

var hDevice: THandle;

if WIN32PLATFORM 0 VER_PLATFORM_WIN32_NT then begin (win95/98>

hDevice := CreateFile(‘\\.\vwin32’, GEMERIC_READ, 0, nil, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);

if (hDevice = INVALID_HANDLE_VALUE) then begin

regs.rEDX := Head * $100 + Ord(Floppy in [‘b’, ‘B’]);

regs.rEAX := $201; // код операции read sector

regs.rEBX := DWORD(buffer); // buffer

regs.rECX := Track * $100 + Sector;

Result := DeviceIoControl(hDevice,VWIN32_DIOC_DOS_INT13, @regs, sizeof(regs), @regs, sizeof(regs), nb, nil)

and ( (regs. rFlags and $1)=0); CloseHandle(hDevice);

begin // Windows NT

if Floppy in I’b’, ‘B’] then DevName[5] := Floppy;

hDevice:= CreateFile(pChar(Devname), GENERIC_READ,

FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) ;

if (hDevice = INVALID_HANDLE_VALUE) then begin

SetFilePointer(hDe vice, (Sector-1)*SectorSize, nil, FILE_BEGIN);(нумерация с 1 >

Result := ReadFile(hDevice, buffer», SectorSize, nb, nil) and (nb=SectorSize);

Функция CreateFile используется и для доступа к портам ввода/вывода. Часто программисты сталкиваются с задачей: как организовать обмен данными с различными нестандартными устройствами, подключенными к параллельному или последовательному порту? В Turbo Pascal для DOS был очень хороший псевдомассив ports: пишешь portix] :- у; и не знаешь проблем. В Win32 прямой доступ к портам запрещен, и приходится открывать их как файлы:

hCom := CreateFile(‘COM2’, GENERIC_READ or GENERIC_WRITE,0, NIL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

if hCom = INVALID_HANDLE_VALUE then

raise EAbort.CreateF Int(‘Ошибка открытия порта: %d’,[GetLastError]);

Самое большое отличие от предыдущего примера — в скромном флаге file_flag_overlapped. О роли этих изменений — в следующем разделе.

Для чтения и записи данных в Win32 используются функции

function ReadFile(hFile: THandle; var Buffer; nNumberOfBytesToRead:

DWORD; var IpNumberOfBytesRead: DWORD; IpOverlapped: POverlapped): BOOL;

function WriteFile(hFile: THandle; const Buffer; nNumberOfBytesToWrite:

DWORD; var IpNumberOfBytesWritten: DWORD; IpOverlapped: POverlapped):

Здесь все сходно с BlockRead и BlockWrite: hFile — это дескриптор файла, Buffer — адрес, по которому будут читаться (писаться) данные; третий параметр означает требуемое число читаемых (записываемых) байт, а четвертый — фактически прочитанное (записанное) . Последний параметр —

IpOverlapped — обсудим чуть ниже.

Отложенный (асинхронный) ввод/вывод

Эта принципиально новая возможность введена впервые в Win32, с появлением реальной многозадачности. Вызывая функции чтения и записи данных, вы на самом деле передаете исходные данные одному из потоков (threads) операционной системы, который и исполняет фактические обязанности по работе с устройством. Время доступа всех периферийных устройств гораздо больше времени доступа к ОЗУ, и ваша программа, вызвавшая Read или Write, будет дожидаться окончания операции ввода/вывода. Замедление работы программы налицо.

Выход был найден в использовании отложенного (overlapped) ввода/вывода. До начала отложенного ввода/вывода инициализируется дескриптор объекта типа события (функция createEvent) и инициализируется структура типа TOverlapped. вы вызываете функцию ReadFile или WriteFile, в которой последним параметром указываете на TOverlapped. Эта структура содержит дескриптор события Windows (event).

ОС начинает операцию (ее выполняет отдельный программный поток, скрытый от программиста) и немедленна возвращает управление приложению: вы можете не тратить время на ожидание. Признак того, что операция началась и продолжается — возврат кода error_io_pending. Пусть вас не пугает слово » e rror» в названии — это совершенно нормально. Если операция продолжается долго (а чтение и запись файлов на дискете, да и на диске, использование именованных каналов можно отнести к «длинным» операциям), то программа может спокойно выполнять последующие операторы. Событие будет «взведено» ОС тогда, когда ввод/вывод закончится.

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

function WaitForSingleObject(hHandle: THandle; dwMilliseconds: DWORD):

Объект ожидания (параметр hHandle) в этом случае — тот самый, который создан нами, указан в структуре TOverlapped и передан в качестве параметра в функцию ReadFile или writeFiie. Можно указать любое время ожидания, в том числе бесконечное (параметр Timeout при этом равен константе infinite). Признаком нормального завершения служит получение кода возврата WAIT_OBJECT_0.

Листинг 6.2 Пример отложенной операции чтения

function TMyClass.Read(var Buffer; Count: Longint): Longint;

var succ : boolean;nb : Cardinal;LastError : Longint;

Overlap.hEvent := CreateEvent(nil, True, False, nil);

succ := ReadFile(FHandle, Buffer, Count, nb, @OverlapRd);

// здесь можно вставить любые операторы, которые

// могут быть выполнены до окончания ввода/вывода

if not succ then begin LastError := GetLastError;

if LastError = ERROR_IO_PENDING then

if WaitForSingleObject(OverlapRd.hEvent, INFINITE)=WAIT_OBJECT_0 then

GetOverlappedResult(FHandle, OverlapRd, nb, TRUE);

end else raise EAbort.Create(Format(‘Read failed, error %d’,[LastError]));

Если вы задали конечный интервал в миллисекундах, а операция еще не закончена, waitForsingleobject вернет код завершения wait timeout. Функция GetOveriappedResult возвращает в параметре nb число байт, действительно прочитанных или записанных во время отложенной операции.

Контроль ошибок ввода/вывода

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

Контроль за ошибками ввода/вывода зависит от применяемых функций. При использовании доступа через Win32 API все функции возвращают код ошибки Windows, который и нужно проанализировать.

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

  • <$I+>— контроль включен (установлен по умолчанию);
  • ($I-) — контроль отключен.

Класс EinOutError отличается тем, что у него есть поле ErrorCode. При возникновении этой исключительной ситуации вы можете получить его значение и принять решение. Основные коды имеют такие значения:


  • 2 — файл не найден;
  • 3 — неверное имя файла;
  • 4 — слишком много открытых файлов;
  • 5- доступ запрещен;
  • 100 — достигнут конец файла;
  • 101 — диск переполнен;
  • 106 — ошибка ввода.

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

function IQResult: integer;

которая возвращает значение 0 при отсутствии ошибок.

Атрибуты файла. Поиск файла

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

Size: Integer; (Размер файла>

FindData: TWin32FindData; (He используется>

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

function FileDateToDateTime(FileDate: Integer): TDateTime;

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

function DateTimeToFileDate(DateTime: TDateTime): Integer;

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

  • faH >скрытый;
  • faSysFile — системный;
  • favolumeiD — метка тома;
  • faDirectory — каталог;
  • faArchive — архивный;
  • faAnyFile — любой.

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

if (SearchRec.Attr AND faReadOnly) > 0

then ShowMessage(‘Файл только для чтения’);

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

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

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

function FindNext(var F: TSearchRec): Integer;

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

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

procedure FindClose(var F: TSearchRec);

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

procedure TFormI.FindBtnClick(Sender: TObject);

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

while FindNext(SearchRec) = 0 do

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

Многие классы VCL имеют унифицированные методы LoadFromStream и SaveToStream, которые обеспечивают обмен данными с потоками. От того, с каким физическим носителем работает поток, зависит место хранения данных.

Базовые классы TStream и THandleStream

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

Класс TStream порожден непосредственно от класса TObject.

Потоки также играют важную роль в чтении/записи компонентов из файлов ресурсов (.dfm). Большая группа методов обеспечивает взаимодействие компонента и потока, чтение свойств компонента из ресурса и запись значений свойств в ресурс (табл. 6.3).

Таблица 6.3, Свойства и методы класса TStream

property Position: Longint ;

Определяет текущую позицию в потоке

property Size: Longint;

Определяет размер потока в байтах

function CopyFrom(Source: TStream; Count: Longint): Longint;

Копирует из потока Source Count байтов, начиная с текущей позиции. Возвращает число скопированных байтов

function Readfvar Buffer; Count: Longint): Longing-virtual; abstract;

Абстрактный класс, перекрываемый в потомках. Считывает из потока Count байтов в буфер Buffer. Возвращает число скопированных байтов

procedure ReadBuffer(var Buffer; Count: Longint);

Считывает из потока Count байтов в буфер Buffer. Возвращает число скопированных байтов

function Seek(Off set: Longint; Origin: Word): Longint; virtual; abstract/

Абстрактный класс, перекрываемый в потомках. Смещает текущую позицию в реальном носителе данных на offset байтов в зависимости от условия Origin (см. ниже)

function Write(const Buffer; Count: Longint): Longint; virtual/abstract;

Абстрактный класс, перекрываемый в потомках. Записывает в поток Count байтов из буфера Buffer. Возвращает число скопированных байтов

procedure WriteBuffer(const Buffer; Count: Longint) ;

Записывает в поток Count байтов из буфера Buffer. Возвращает число скопированных байтов

function ReadComponent(Instance: TComponent): TComponent;

Передает данные из потока в компонент Instance, заполняя его свойства значениями

function ReadComponentRes(Instance: TComponent): TComponent;

Считывает заголовок ресурса компонента instance и значения его свойств из потока

Считывает заголовок ресурса компонента из потока

procedure WriteComponent(Instance: TComponent) ;

Передает в поток значения свойств компонента Instance

procedure WriteComponentRes(const ResName: string; Instance: TComponent);

Записывает в поток заголовок ресурса компонента instance и значения его свойств

Итак, в основе операций считывания и записи данных в потоке лежат методы Read и Write. Именно они вызываются для реального выполнения операции внутри методов ReadBuffer И WriteBuffer, ReadComponent И WriteComponent.

Так как класс TStream является абстрактным, то методы Read и write также являются абстрактными. В классах-потомках они перекрываются, обеспечивая работу с конкретным физическим носителем данных.

Метод seek используется для изменения текущей позиции в потоке. «Точка отсчета» позиции зависит от значения параметра origin:

  • soFro mBeginning — смещение должно быть положительным и отсчитывается от начала потока
  • soFromCurrent — смещение относительно текущей позиции в потоке
  • soFromEnd — смещение должно быть отрицательным и отсчитывается от конца потока

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

  • метод ReadResHeader для считывания заголовка ресурса компонента из потока;
  • метод ReadComponent для считывания значений свойств компонента.

Для записи ресурса в поток применяется метод writeComponentRes.

Класс THandieStream инкапсулирует поток, связанный с физическим носителем данных через дескриптор.

Для создания потока используется конструктор

constructor Create(AHandle: Integer);

в параметре которого передается дескриптор. Впоследствии доступ к дескриптору осуществляется через свойство:

property Handle: Integer;

Класс TFileStream позволяет создать поток для работы с файлами. При этом поток работает с файлом без учета типа хранящихся в нем данных (см. выше).

Полное имя файла задается в параметре Filename при создании потока:

constructor Create(const FileName: string; Mode: Word);

Параметр Mode определяет режим работы с файлом. Он составляется из флагов режима открытия:

  • fmcreate — файл создается;
  • fmOpenRead — файл открывается для чтения;
  • fmopenwrite — файл открывается для записи;
  • fmOpenReadWrite — файл открывается для чтения и записи;

и флагов режима совместного использования:

  • fmShareExclusive — файл недоступен для открытия другими приложениями;
  • fmshareDenyWrite — другие приложения могут читать данные из файла;
  • fmShareDenyRead — другие приложения могут писать данные в файл;
  • fmShareDenyNone — другие приложения могут производить с файлом любые операции.

Для чтения и записи из потока используются методы Read и Write, унаследованные от класса HandieStream:

procedure TFormI.CopyBtnClick(Sender: TObject);

var Streami, Stream2: TFileStream;

IntBuf: array[0..9] of Integer;

begin if Not OpenDIg.Execute then Exit;

try Streami := TFileStream.Create(OpenDlg.FileName, fmOpenRead);

try Stream2 := TFileStream.Create(‘TextFile.tmp’, fmOpenWrite);

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

При необходимости копирования одного файла в другой целиком используется метод СopyFrom, унаследованный от TStream:

procedure TFormI.CopyBtnClick(Sender: TObject);

var Streami, Stream2: TFileStream;

if Not OpenDlg.Execute then Exit;

Stream1 := TFileStream.Create(OpenDlg.FileName, fmOpenRead);

Stream2 := TFileStream.Create(‘Sample.tmp’, fmOpenWrite);

Обратите внимание, что в данном случае для определения размера передаваемого потока необходимо использовать свойство Stream.size, которое дает реальный объем данных, содержащихся в потоке.Функция SizeOf (Stream) в этом случае даст размер объекта потока, и не более того.

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

property Memory: Pointer;

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

procedure SetSize(NewSize: Longint); override;

Для очистки памяти потока используется метод

Чтение/запись данных в память выполняется привычными методами Read и

Wr ite. Также запись данных в память может осуществляться методами:

  • procedure LoadFromFile(const FileName: string); -из файла;
  • procedure LoadFromStream(Stream: TStream); из другого потока.

Дополнительно можно использовать методы записи данных в файл или поток:

  • procedure SaveToFile(consL FileName: string);
  • procedure SaveToStream(Stream: TStream);

Так как строковые константы и переменные широко применяются при разработке приложений, то для удобства работы с ними создан специальный класс TStringStream. Он обеспечивает хранение строки и доступ к ней во время выполнения приложения.

Он обладает стандартным для потоков набором свойств и методов, добавляя к ним несколько, упрощающих использование строк.

Свойство только для чтения

property DataString: string;

обеспечивает доступ к хранимой строке. Методы

function Readfvar Buffer; Count: Longint): Longint; override;

function Write(const Buffer; Count: Longint): Longint; override;

реализуют обычный для потоков способ чтения и записи строки для произвольной переменной Buffer.

function ReadString(Count: Longint): string;

обеспечивает чтение Count байтов строки потока, начиная с текущей позиции. Метод

procedure WriteString(const AString: string);

дописывает к строке строку AString, начиная с текущей позиции.

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

Класс EFCreateError возникает при ошибке создания файла, a EFOpenError — при открытии файла.

При чтении/записи данных в поток могут возникнуть ИС ERead Error и EWriteError.

Оповещение об изменениях в файловой системе

Многие программисты задавались вопросом: как получить сигнал от операционной системы о том, что в файловой системе произошли какие-то изменения? Такой вид оповещения позаимствован из ОС Unix и теперь доступен программистам, работающим с Win32.

Для организации мониторинга файловой системы нужно использовать три

функции — FindFirstChangeNotification, FindNextChangeNotification и FindcioseChangeNotification. Первая из них возвращает дескриптор объекта файлового оповещения, который можно передать в функцию ожидания. Объект активизируется тогда, когда в заданной папке произошли те или иные изменения (создание или уничтожение файла или папки, изменение прав доступа и т. д.). Вторая — готовит объект к реакции на следующее изменение. Наконец, с помощью третьей функции следует закрыть ставший ненужным объект.

Так может выглядеть код метода Execute потока, созданного для мониторинга:

var DirName : string;

repeat r := WaitForSingleObject(fn,2000);

if r = WAIT_OBJECT_0 then Formi.UpdateList;

if not FindNextChangeNotification(fn) then break;

В главной форме должны находиться компоненты, нужные для выбора обследуемой папки, а также компонент TListBox, в который будут записываться имена файлов:

procedure TFormI.ButtonlClick(Sender: TObject);

var dir : string;

if SelectDirectory(dir,[ ],0) then begin Editl.Text := dir;

var SearchRec: TSearchRec;

FindFirst(Editl.Text+’\*.*’, faAnyFile, SearchRec);

until FindNext(SearchRec) <> 0;

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

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

Последний, самый нетрадиционный, вид работы с файлами — это так называемые отображаемые файлы.

Вообще говоря, в 32-разрядной Windows под «памятью» подразумевается не только оперативная память (ОЗУ), но также и память, резервируемая операционной системой на жестком диске. Этот вид памяти называется виртуальной памятью. Код и данные отображаются на жесткий диск посредством страничной системы (paging system) подкачки. Страничная система использует для отображения страничный файл (win386.swp в Windows 95/98 и pagefiie.sys в Windows NT). Необходимый фрагмент виртуальной памяти переносится из страничного файла в ОЗУ и, таким образом, становится доступным.

А что, если так же поступить и с любым другим файлом и сделать его частью адресного пространства? В Win32 это возможно. Для выделения фрагмента памяти должен быть создан специальный системный объект Win32, называемый отображаемым файлом. Этот объект «знает», как соотнести файл, находящийся на жестком диске, с памятью, адресуемой процессами.

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

Создание и использование объектов файлового отображения осуществляется посредством использования функций Windows API. Этих функций три:

  • CreateFileMapping
  • MapViewOfFile
  • UnMapViewOfFile

Отображаемый файл создается операционной системой при вызове функции CreateFileMapping. Этот объект поддерживает соответствие между содержимым файла и адресным пространством процесса, использующего этот файл. Функция CreateFileMapping имеет шесть параметров:

function CreateFileMapping(hFile: THandle;

flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWORD;

IpName: PChar): THandle;

Первый параметр имеет тип THandle. Он должен соответствовать дескриптору уже открытого при помощи функции CreateFile файла. Если значение параметра hFile равно $ffffffff, то это приводит к связыванию объекта файлового отображения со страничным файлом операционной системы.

Второй параметр — указатель на запись типа TSecurityAttributes. При отсутствии требований к защите данных в Windows NT значение этого параметра всегда равно nil. Третий параметр имеет тип dword. Он определяет атрибут защиты. Если при помощи отображаемого файла вы планируете совместное использование данных, третьему параметру следует присвоить значение PAGE_READWRITE.

Четвертый и пятый параметры также имеют тип dword. Когда выполняется функция CreateFileMapping, значение типа dword четвертого параметра сдвигается влево на четыре байта и затем объединяется со значением пятого параметра посредством операции and. Проще говоря, значения объединяются в одно 64-разрядное число, равное объему памяти, выделяемой объекту файлового отображения из страничного файла операционной системы. Поскольку вы вряд ли попытаетесь осуществить выделение более чем четырех гигабайт данных, то значение четвертого параметра всегда должно быть равно нулю. Используемый затем пятый параметр должен показывать, сколько памяти в байтах необходимо зарезервировать в качестве совместной. Если вы хотите отобразить весь файл, четвертый и пятый параметры оба должны быть равны нулю.

Шестой параметр имеет тип pchar и представляет собой имя объекта файлового отображения.

Функция CreateFileMapping возвращает значение типа THandle. В случае успешного завершения возвращаемое функцией значение представляет собой дескриптор созданного объекта файлового отображения. В случае возникновения какой-либо ошибки возвращаемое значение будет равно 0.

Следующая задача — спроецировать данные файла в адресное пространство нашего процесса. Этой цели служит функция MapviewOfFiie. Функция MapviewOfFile имеет пять параметров:

function MapViewOfFile(hFileMappingObject: THandle;

dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap: DWORD):

Первый параметр имеет тип THandle. Его значением должен быть дескриптор созданного объекта файлового отображения — тот, который возвращает функция CreateFileMapping. Второй параметр определяет режим доступа к файлу: FILE_MAP_WRITE, FILE_MAP_READ или FILE_MAP_ALL_ACCESS.

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

Пятый и последний параметр функции MapviewOfFile, как и предыдущие параметры, имеет тип dword. Он используется для определения (в байтах) количества данных объекта файлового отображения, которые надо отобразить в процесс (сделать доступными для вас). Для достижения наших целей это значение должно быть установлено нулем, что означает автоматическое отображение в процесс всех данных, выделенных перед этим функцией

Значение, возвращаемое функцией MapviewOfFile, имеет тип «указатель». Если функция отработала успешно, то она вернет начальный адрес данных объекта файлового отображения.

Следующий фрагмент кода демонстрирует вызов функции MapviewOfFile:

hMappedFile := CreateFileMapping(FHandle, nil, PAGE_READWRITE, 0, 0, ‘SharedBlock’);

if (hMappedFile = 0) then

pSharedBuf := MapviewOfFile(hMappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);

if (pSharedBuf = nil) then

ShowMessage (‘MapView error’);

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

Последние две функции, имеющие отношение к объекту файлового отображения, называются UnMapViewOfFile И CloseHandle. функция

UnMapviewOf File делает то, что подразумевает ее название. Она прекращает отображение в адресное пространство процесса того файла, который перед этим был отображен при помощи функции MapviewOfFile. Функция CloseHandle закрывает дескриптор объекта файлового отображения, возвращаемый функцией CreateFileMapping.

Функция UnMapViewOfFile должна вызываться перед функцией CloseHandle.

Функции UnMapViewOfFile передается единственный параметр типа указатель:

procedure TClientForm.FormDestroy(Sender: TObject);

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

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

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

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

Затруднительно было бы сказать, при изучении каких глав будет полезен материал этой главы. Скорее всего, он понадобится везде.

DelphiComponent.ru — бесплатно видеоуроки по Delphi, статьи, исходники

Файлы в Delphi: Чтение данных из файла

Чтение из файла выполняется при помощи инструкций read и readln, которые в общем виде записываются следующим образом:

  • ФайловаяПеременная — переменная типа Textfile;
  • списокпеременных — имена переменных, разделенные запятыми.

Закажи видеокурс по Delphi прямо сейчас и получи 106 видеоуроков:

ЗАКАЗАТЬ

Посмотрите подробный видеоурок по работе с файлами в Delphi

Скачайте больше видеоуроков по Delphi бесплатно прямо сейчас — скачать.

Чтение чисел

Следует понимать, что в текстовом файле находятся не числа, а их изображения. Действие, выполняемое инструкциями read или readin, фактически состоит из двух: сначала из файла читаются символы до появления разделителя (пробела или конца строки), затем прочитанные символы, являющиеся изображением числа, преобразуются в число, и полученное значение присваивается переменной, имя которой указано в качестве параметра инструкции read ИЛИ readln.

Например, если текстовый файл a:\data.txt содержит следующие строки:

то в результате выполнения инструкций:

AssignFile(f, ‘c:\data.txt’ );
Reset(f); // открыть для чтения
readln(f, a);
readln(f, b);
readln(f, с);
readln(f, d);

значения переменных будут следующими: а = 23, b = 15, с = 45, d = 23.

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

Поэтому в результате выполнения инструкций

AssignFile (f, ‘a: \data. txt’ );
Reset(f);
readln(f, a);
readln(f, b, c);
readln (f, d) ;

значения переменных будут следующими: а = 23, b = 45, с = 28, d = 56.

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

Чтение строк

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

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

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

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

Пусть, например, текстовый файл freinds.txt содержит строки:

Конец файла

Пусть на диске есть некоторый текстовый файл. Нужно в диалоговое окно вывести содержимое этого файла. Решение задачи довольно очевидно: надо открыть файл, прочитать первую строку, затем вторую, третью и т. д. до тех пор, пока не будет достигнут конец файла. Но как определить, что прочитана последняя строка, достигнут конец файла? Для определения конца файла можно воспользоваться функцией EOF (End Of File — конец файла).

У функции EOF один параметр — файловая переменная. Значение функции EOF равно False, если прочитанный элемент данных не является последним в файле, т. е. возможно дальнейшее чтение. Если прочитанный элемент данных является последним, то значение EOF равно True.

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

В листинге 7.5 приведена процедура, которая выполняет поставленную задачу. Она читает строки из файла, имя которого ввел пользователь во время работы программы, и выводит эти строки в поле Memo. Окно программы приведено на рис. 7.6.

Листинг 7.5. Чтение из файла

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

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

Добавление очередной прочитанной из файла строки в поле Memo выполняется применением метода Add к свойству Lines.

Учебно-методическое пособие на тему «Файлы в Delphi».

Учебно-методическое пособие с теорией, тестом и лабораторной работой.

Содержимое разработки

Учебно-методическое пособие по теме «Файлы в Delphi».

Аглиуллина Айгуль Ильнуровна

педагог дополнительного образования

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

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

Delphi — это среда быстрой разработки, в которой в качестве языка программирования используется язык Delphi. Язык Delphi — строго типизированный объектно-ориентированный язык, в основе которого лежит хорошо знакомый программистам Object Pascal.

В настоящее время программистам стала доступна очередная версия пакета Delphi — Borland Delphi 7 Studio. Она может работать в среде операционных систем от Windows 98 до Windows XP. Особых требований, по современным меркам, к ресурсам компьютера пакет не предъявляет.

В Delphi имеется большое количество различных производных и непроизводных типов. Существенной особенностью всех значений производных типов является наличие в них конечного, наперед заданного числа компонент. Например, в значении многомерного массива это число можно определить, зная количество компонент по каждому измерению, а в значении записи это число определяется количеством и типом полей. Таким образом, заранее, еще до выполнения программы, по этому описанию можно выделить необходимый объем памяти машины для хранения значений переменных этих типов. Но существует определенный класс задач и определенные ситуации, когда количество компонент (пусть даже одного и того же типа) заранее определить невозможно, оно выясняется только в процессе решения задачи. Поэтому возникает необходимость в специальном типе значений, которые представляют собой произвольные последовательности элементов одного и того же типа, причем длина этих последовательностей заранее не определяется, а конкретизируется в процессе выполнения программы. Этот тип значений получил название файлового типа.

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

1. Понятие файла

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

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

В зависимости от типа элементов различают три вида файла:

файл из элементов фиксированного размера (элементами такого файла чаще всего являются записи);

файл из элементов переменного размера (нетипизированный файл) — такой файл рассматривается просто как последовательность байтов;

текстовый файл; элементами такого файла являются текстовые строки.

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

Для работы с файлом, состоящим из типовых элементов переменная объявляется с помощью словосочетания file of, после которого записывается тип элемента:

имя_файла:file of тип_элементов_файлов;

Объявление переменной для работы с нетипизированным файлом выполняется с помощью отдельного слова file:

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

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

2. Компоненты, работающие с файлами

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

Для начала необходимо упомянуть компоненты Delphi, которые умеют работать с файлами. Они читают и сохраняют своё содержимое, строки типа String, в файл текстового формата. Это компоненты ListBox, ComboBox и Memo, расположенные на первой же вкладке палитры компонентов.

Каждая строка компонентов ListBox и ComboBox является объектом Items[i], а Memo — Lines[i], где i — номер строки, который отсчитывается от нуля. Добавление строк в компоненты выполняется методами Add и Insert:

Рисунок №1. Добавление строк в компоненты

begin
Memo1.Lines.Add(‘Первая строка’);
ComboBox1.Items.Add(‘Первая строка’);
ComboBox1.Items.Add(‘Вторая строка’);
ListBox1.Items.Add(‘Первая строка’);
ListBox1.Items.Add(‘Вторая строка’);
end ;

Метод Add добавляет новую строку в конец. Метод Insert имеет дополнительный параметр, указывающий, после какой строки разместить новую строку. Доступ к строкам осуществляется так:

ComboBox1.Items[0] := ‘Первая строка изменилась‘ ;

ListBox1.Items[1] := ‘Вторая строка изменилась‘ ;

У компонента ComboBox дополнительно есть свойство Text, где (как и у компонента Edit) находится вводимый текст:

ComboBox1.Text := ‘ Вводимый текст ‘;

На выделенную в данный момент строку компонента ComboBox указывает свойство ItemIndex типа Integer, то есть это номер выделенной строки. Следовательно, получить саму выделенную строку компонента ComboBox можно следующей конструкцией:

S:=ComboBox1.Items[ComboBox1.ItemIndex];
или, пользуясь оператором присоединения

Таким способом по нажатию клавиши Enter можно заносить в этот компонент вводимую в строку информацию и удалять нажатием Escape:
выделить на Форме ComboBox и перейти в Инспектор объектов, на вкладку Events. Щёлкните дважды по обработчику OnKeyPress. Система Delphi создаст заготовку обработчика.

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

Впрочем, расширение можно поставить любое, не обязательно .txt, как и вообще без него обойтись. Но расширение .txt позволит легко открыть файл стандартным Блокнотом, что бывает очень удобно на этапе написания программы.
Для загрузки служит метод LoadFromFile:

3. Классическая работа с файлами

Технология работы с файлами в системе Delphi требует определённого порядка действий:

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

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

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

В Delphi реализовано несколько способов работы с файлами. Рассмотрим классический способ. Файловая переменная вводится для указания на файл. Делается это с помощью ключевого слова File :

Описанная таким образом файловая переменная считается нетипизированной, и позволяет работать с файлами с неизвестной структурой. Данные считываются и записываются побайтно блоками, размер которых указывается при открытии файла, вплоть от 1 байт.

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

var F: File of тип_записи ;

В качестве типа могут использоваться базовые типы, или создаваться свои. Важно только, чтобы для типа был точно известен фиксированный размер в байтах, поэтому, например, тип String в чистом виде применяться не может, а только в виде String[N].

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

Для текстовых файлов отдельно укажем, что тип файловой переменной в TextFile, а тип обычной — String.

Для открытия файла нужно указать, где он расположен. Для этого файловая переменная должна быть ассоциирована с нужным файлом, который определяется его адресом. Адрес файла может быть абсолютным, с указанием диска и каталогов (‘C:\Мои документы\Мои рисунки\FileName.ini’), или относительным, тогда он создаётся в папке с .exe файлом программы. Для задания относительного адреса достаточно указать имя файла с нужным расширением. Делается это оператором AssignFile:

AssignFile(SaveF, ‘C:\Мои документы\Мои рисунки\FileName.ini’);

Теперь файл должен быть открыт.

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

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

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

Чтение файла производится оператором Read:

Запись в файл производится оператором Write:

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

then Read(SaveF, SaveV);

Принудительно установить указатель на нужную запись можно оператором Seek(SaveF, N), где N — номер нужной записи, который, как и почти всё в программировании, отсчитывается от нуля:

Seek(SaveF, 49); — установка указателя на 50-ю запись.

При последовательном чтении из файла рано или поздно будет достигнут конец файла, и при дальнейшем чтении произойдёт ошибка. Проверить, не достигнут ли конец файла, можно оператором EOF (аббревиатура End Of File), который равен true, если прочитана последняя запись и указатель находится в конце файла:

while (not EOF(SaveF)) do

Для текстовых файлов вместо Read и Write используются операторы Readln и Writeln, умеющие определять конец строки.

Оператор Truncate(SaveF) позволяет отсечь (стереть или удалить) все записи файла, начиная от текущей позиции указателя, и до конца файла.

В конце работы с файлом его необходимо закрыть. Это делается оператором CloseFile(SaveF) ;

4. Работа с текстовыми файлами

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

Приступая к работе с файлом, нужно первым делом вызвать процедуру AssignFile, чтобы файловой переменной поставить в соответствие имя файла на диске:

В результате этого действия поля файловой переменной F инициализируются начальными значениями. При этом в поле имени файла заносится строка ‘MyFile.txt’.

Так как файла еще нет на диске, его нужно создать:

Теперь запишем в файл несколько строк текста. Это делается с помощью хорошо вам знакомых процедур Write и Writeln:

Writeln(F, ‘Pi = ‘, Pi);

Writeln(F, ‘Exp = ‘, Exp(1));

При работе с файлами первый параметр этих процедур показывает, куда происходит вывод данных.

После работы файл должен быть закрыт:

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

Сохраненное ключевое слово в Delphi

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

Какова цель ключевого слова и что он делает?

Из моего файла справки Delphi 7:

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

Необходимо сохранить сохраненную директиву по True, False, имя булева поле или имя без параметров метод, возвращающий логическое значение. Например,

Если свойство не имеет хранимой директивы, он обрабатывается так, как будто сохраненные True были указано.

Похоже, что он контролирует, сохранять или не сохранять свойство, относящееся к компоненту в файле .DFM для формы. (Только предположение, хотя)

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

Хранимая директива принимает логическое значение: метод, который возвращает логический результат, ссылку на поле логического типа или константное выражение логического типа. RTT надлежащего записывает смещение поля, ссылку на метод или постоянное значение, и Delphis IDE использует эту информацию, чтобы решить, следует ли пропустить свойство из файла.dfm.

Среда IDE вызывает метод, проверяет значение полей или использует постоянное логическое значение, и если значение равно False, свойство не сохраняется в файл.dfm. Если сохраненное значение равно True, происходит поведение по умолчанию, а именно, что свойство сохраняется, если его значение отличается от значения по умолчанию.
Секреты и уловки

Посмотрите другие вопросы по метке delphi или Задайте вопрос

Зарезервированные слова Delphi

var A, B : Integer; begin A:=3; B:=4; A:=A*A+B*B; end;

if (условие) then (действие) else (альтернатива) ;

Слова if (если), then (тогда), else (иначе) — зарезервированные. Действие и else альтернатива — это любые операторы Delphi, или несколько операторов, заключённых в логические скобки begin/end, или вызов подпрограммы. Если условие истинно, то выполняется действие, если ложно, то выполняется альтернатива.

(применяется, когда известно количество повторений цикла)

for счётчик := выражение-1 to выражение-2 do действие ;

Возможна работа оператора цикла, при котором переменная-счётчик будет не увеличиваться, а уменьшаться. В этом случае ключевое слово to заменяется на downto:

for счётчик := выражение-1 downto выражение-2 do действие ;

Цикл с предусловием (применяется, когда неизвестно количество повторений цикла)

while условие do тело цикла ;

Этот цикл будет выполняться до тех пор, пока истинно условие (логическое выражение, возвращающее значение типа Boolean). При этом если это выражение сразу равно false, тело цикла не будет выполнено ни разу. Нужно очень внимательно следить за написанием условия и контролем завершения цикла, так как в результате ошибки цикл while будет повторяться бесконечное количество раз, что приведёт к «зацикливанию» и «зависанию» программы.

Цикл с постусловием

(применяется, когда неизвестно количество повторений цикла)

repeat тело цикла until условие ;

Повторения сначала выполняет тело цикла, а затем уже проверяет выполнение условия: Таким образом, этот вариант цикла гарантирует, что тело цикла будет выполнен по крайней мере один раз. И будет выполняться до тех пор, пока условие не станет истинным (т.е. true). Стоит отметить, что это единственный оператор Delphi, в котором тело цикла не требуется заключать в логические скобки begin/end. Начало и конец тела цикла определяются по ключевым словам repeat и until.

Позволяет изменить последовательность выполнения программы. В качестве метки может использоваться любой допустимый идентификатор или число в диапазоне от 0 до 9999. Метку предварительно необходимо объявить в разделе описания переменных, но с помощью не ключевого слова var, а ключевого слова label:

Сохраненное ключевое слово в Delphi

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

Какова цель ключевого слова и что оно делает?

3 ответа

Из моего файла справки Delphi 7:

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

За сохраненной директивой должны следовать True, False, имя логического поля или имя метода без параметров, который возвращает логическое значение. Например,

Если свойство не имеет хранимой директивы, оно обрабатывается так, как если бы было сохранено значение True.

Звучит так, как будто он контролирует, сохранять ли свойство, относящееся к компоненту, в файле .DFM для формы. (Просто предположение, хотя)

Это ключевое слово определяет, следует ли сохранять значение свойства в файле формы; это true по умолчанию. Может быть полезно избежать, например, сохранения больших кусков двоичной информации в вашем файле .dfm (например, компонент изображения, который должен читать свое содержимое только во время выполнения.)

Хранимая директива принимает логическое значение: метод, который возвращает логический результат, ссылку на поле логического типа или константное выражение логического типа. RTTI свойства записывает смещение поля, ссылку на метод или постоянное значение, и среда IDE Delphi использует эту информацию, чтобы решить, следует ли пропустить свойство из файла .dfm.

Среда IDE вызывает метод, проверяет значение поля или использует постоянное логическое значение, и если значение равно False, свойство не сохраняется в файл .dfm. Если сохраненное значение равно True, происходит поведение по умолчанию, а именно, что свойство сохраняется, если его значение отличается от значения по умолчанию.

Секреты и уловки

Сохраненная директива часто неправильно понимается. Установка для True значения не заставляет Delphi сохранять значение свойства в файле .dfm. True — это значение по умолчанию для хранимой директивы. Вместо этого все, что вы можете сделать, это опустить свойство из файла .dfm, установив для параметра false значение False.

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

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

Ссылка на поле может быть именем поля, элементом записи или элементом массива с постоянным индексом. Ссылка на поле должна иметь логический тип.

File — Ключевое слово Delphi

Текстовый файл отличается тем что он разбит на разные по длине строки, отделенные символами #13#10. Есть 2 основных метода работы с текстовыми файлами — старый паскалевский способ и через файловые потоки. У обоих есть преимущества и недостатки. Через потоки способ проще поэтому начнем с него.

Итак у всех потомков класса TStrings (TStringList, memo.Lines и т.п. ) есть методы записи и чтения в файл — SaveToFile, LoadFromFile. Преимущество — простота использования и довольно высокая скорость, недостаток — читать и писать файл можно только целиком.

1. Загрузка текста из файла в Memo:

2. Сохранение в файл:

3. А вот так можно прочитать весь файл в строку:

Паскалевский метод доступа

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

Итак, для доступа к текстовым файлам используется переменная типа TextFile. До сих пор не совсем понимаю что это такое физически — что-то типа «внутреннего» паскалевского Handle на файл.

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

1) Определяем файловую переменную:

2) Ассоциируем ее:

3) Теперь надо этот файл открыть, есть 3 варианта:

  1. файла нет или он должен быть перезаписан, открытие для записи: Rewrite(f)
  2. файл есть и его надо открыть для чтения (с первой строки): Reset(f)
  3. файл есть и его надо открыть для дописования строк в конец: Append(f)

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

  • Перезаписать весть файл
  • Читать с первой строки
  • Дописать что-то в конец
  • Читать и писать файл целиком (см. выше работу через TStrings)

В конце работы открытый файл нужно закрыть:

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

Чтение предварительно открытого файла:

ReadLn(f, s) — будет прочитанна текущая строка и позиция чтения переведена на следующую позицию.

А как прочитать весь файл?

Хорошо, а если файл несколько метров есть ли способ поставить какой-нибудь ProgressBar или Gauge чтобы показывал сколько считанно? Есть, но не совсем прямой — не забыли, сколько строк в файле заранее мы не знаем, узнать можно только прочитав его весь, но показометер мы все-таки сделаем:

Теперь комментарии к коду.

  1. Функию GetFileSize я рсссмотрю после, она немного по другому подходит к чтению файла (кстати я знаю еще по крайней мере 3 способа ее реализации, поэтому не нужно указывать что это можно сделать легче, быстрее или просто по другому — просто давайте разберем это позже)
  2. Переменная i — все время указывает на количество байт которое мы считали — мы определяем длину каждой строки и прибавляем 2 (символы конца строки). Зная длину файла в байтах и сколько байт прочитано можно оценить и прогресс, но
  3. Если ставить изменение прогресса после каждой строки, то это очень сильно тормознет процесс. Поэтому вводим переменную j и обновляем прогресс например 1 раз на 1000 прочитанных строк
  4. Переменная Canceled — глобальная переменная. Поставьте на форму кнопку, в обработчике нажатия поставьте Canceled:=True; и нажатие кнопки прервет чтение файла.

Теперь как писать в текстовый файл:

Запись целой строки:

Запись кусочка строки(те следующая операция записи будет произведена в ту же строку):

Если переменная s содержит больше 255 символов (т.е. является длинной строкой), то таким способом ни фига не запишится, в файл вместо строки попадут 4 байта указателя на нее. Надо делать так:

Работа через WinAPI

Раздел написан Podval (примеры к сожалению на С++)

Любителям WinAPI посвящается. Функции FileOpen, FileSeek, FileRead. Возьмем форму, положим на нее кнопку, грид и Опен диалог бокс. Это для Билдера, но какая нам в данном случае разница?

Потренируемся еще. Функции FileExists, RenameFile, FileCreate, FileWrite, FileClose. Бросим на форму Save dialog box.

(с) Оба примера взяты из хелпа по Borland C++ Builder 5.

Первоисточник тот же.

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

В этом примере идет поиск в текущем каталоге и каталоге Windows

В дополнение к Дате/Времени

Для конвертации возвращаемого значения в TDateTime:

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

Объявляем файл байтов:

Ассоциируем файловую переменную с физическим файлом:

Теперь мы можем либо перезаписать/создать файл:

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

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

Теперь функции работы с файлом:

Все эти функции не работают с файлами большими 2 Gb.

После работы файл надо закрыть:

Приведенные выше механизмы будут работать с любым файлом, так как любой файл можно считать файлом байтов. Теперь где это можно использовать? В принципе везде, но в подавляющем большинстве случаев это будет очень неудобно, ведь скорость считывания при чтении по байтам будет на порядки более низкой чем другими способами. Однако в некоторых случаях этот способ может быть очень полезен. Например в программе вам надо заменить 100й байт файла на другой, или прочитать 100й байт файла, например во всяких читерских программах, при взломе и т.п. Здесь такой доступ будет весьма удобен. Гораздо более интересным представляется дальнейшее развитие технологии типизированных файлов (их еще лет 15 назад называли «Файлы прямого доступа»). Представим себе, что файл состоит не из байт а из более сложных структур. Например мы имеем некоторую информацию в виде:

Обратите внимание, что все элементы записи точно типизированны, нет ни длинных строк, ни открытых массивов, ни объектов, другими словами, заранее точно известно сколько именно байт будет занимать переменная этого типа. Объявим переменную этого типа:

и файл этого типа:

Теперь мы можем читать и писать сразу целую структуру, абсолютно так же как и если бы это был один байт:

Все остальные функции приведенные в предыдущей статье будут работать так же, только одно отличие — Seek и Size оперируют не с количеством байт, а с количеством записей.

Идем дальше. Есть такое понятие как нетипизированный файл. Это такой файл который содержит разнородные элементы. Например файл EXE — вначале он имеет заголовок, затем двоичный код, в конце какие-то ресурсы. Все части файла имеют разную длину и разную структуру. Тут уже обратится к произвольному элементу сложно, обычно надо вначале узнать где этот элемент находится, подчас это записано в предыдущем куске информации. Работа с такими файлами достаточно сложна и требует вручную разработки алгоритмов его чтения, но в связи гибкостью структуры и компактностью такие файлы составляют большинство. Для работы с нетипизированными файлами используют процедуры BlockRead и BlockWrite, которые позволяют читать/писать произвольное количество байт. Привожу пример пользования этими функциями из справки по Дельфи:

Этот код копирует из одного файла в другой. Замечания по поводу этого метода работы с файлами — плюсы — очень высокая скорость, особенно если размер буффера увеличить до 64kb-512kb, что позволит считывать файл достаточно большими кусками, чтобы обеспечить отсутствие простоев винчестера, к тому же обеспечивается очень высокая гибкость в работе. Минусы — сложность разработки, необходимость вручную писать все детали механизма чтения/записи и интерпретации данных.

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

Теперь разберем возможности работы потомка TStream — TFileStream — файловый поток. Этот класс был специально введен для работы с файлами. Для работы с файловым потоком Вам надо записать в Uses модули classes, Sysutils (classes — включает в себя собственно определение класса, Sysutils — некоторые константы необходимые для работы).

Вот пример записи/перезаписи файла:

Теперь небольшой разбор:

TFileStream.create — конструктор класса, его вызов требует указания имени файла и опций его открытия, следующие опции определены:

Теперь метод Write — этим методом в файл пишется любая информация из буфера любого типа, Вам надо указать только буффер и количество записываемых байтов. В данном случае используется переменная типа String в качестве буффера, но так как для длинных строк она представляет собой лишь указатель, то конструкция «pointer(s)^» заставляет обращаться именно к ее содержимому.

А вот этот код демонстрирует чтение файла с использованием файлового потока:

И пояснения к коду:

  1. Никаких проверок длину файла и его наличие здесь не делается — это демонстрационный код, а не готовая процедура чтения.
  2. Файл мы считываем в буффер типа PChar (с тем же успехом можно использовать массив или любой другой контейнер). Для тех кто не помнит — процедуры GetMem(p, 255) и FreeMem(p) — распределение памяти для строки и освобождение памяти.
  3. Метод потока Seek позволяет установить текущую позицию считывания/записи файла. Первый параметер — номер байта, второй — это от чего считать этот байт (у нас считать от начала файла), возможны варианты:
    • soFromBeginning — от начала файла
    • soFromCurrent — от текущей позиции считывания
    • soFromEnd — от конца файла (в этом случае номер байта должен быть отрицательным или равным нулю)
  4. Собственно считывание из потока осуществляется методом read, в котором указывается в качестве параметров буфер в который мы читаем и желаемое количество байт для чтения. Метод read является функцией, которая возвращает количество байт реально прочитанных из потока.

Заканчивая о файловых потоках хочу упомянуть о методе

CopyFrom который позволяет перекачивать информацию из одного потока в другой и о свойствах:

Size — размер файла
Position — текущая позиция чтения/записи потока

Работа с файловыми потоками весьма быстра, этот класс, являсь классом VCL, в то же время базируется на низкоуровневых функциях Windows, что обеспечивает очень высокую скорость работы и стабильность операций. К тому же многие компоненты и классы VCL поддерживаю прямое чтение и запись с файловыми потоками, что занчительно упрощает работу — например TStringList, TBlobField, TMemoField и другие.

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

Работа через Handle

Еще один способ работы с файлами — это открытие Handle на файл и работу через него. Тут есть 2 варианта — можно использовать функции Дельфи или использовать WinAPI напрямую.

При использовании функций Дельфи можно применять следующие функции:

FileOpen(FileName, fmOpenWrite or fmShareDenyNone) — функция открывает файл и возвращает целое цисло — Handle на файл. Параметры функции — имя файла и тип доступа (все типы доступа я перечислил ранее). Если файл успешно открыт то Handle должен быть положительным цислом, отрицательное число — это код ошибки.

Во всех остальных функциях используется именно значение Handle, возвращаемое этой функцией.

FileClose(Handle: Integer) — закрывает файл

FileRead(Handle: Integer; var Buffer; Count: Integer): Integer;
FileWrite(Handle: Integer; const Buffer; Count: Integer): Integer;

Эти функции для чтения/записи файла, где Buffer любая переменная достаточного размера для чтения/записи куска информации (обычно типа PChar или массив), Count-количество байт, которое Вы желаете записать/прочитать. Функции возвращают количество байт которые реально были прочитанны или записаны.

Этот тип доступа к файлам применяется весьма редко. Дело в том что он практически дублирует соответствующие функции WinAPI и к тому же обычно работает несколько медленнее, чем например потоки. И все же использование функций FileOpen и FileClose не лишено привлекательности. Наряду с тем что эти функции намного легче в использовании соответствующих функций WinAPI (можете сравнить — FileOpen имеет 2 параметра, cooтветствующая функция WinAPI — CreateFile имеет 7 параметров, большая часть из которых реально требуется лишь в ограниченном числе случаев) этот путь доступа открывает возможность прямого использования всех функций WinAPI про работе с файлами, которые требуют Handle на открытый файл.

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

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

ChDir(NewCurrentPath: string); — изменяет текущий каталог (в среде Windows сие конечно не так актуально как в ДОС, но все же), прочитать же текущий каталог можно функцией GetCurrentDir, а текущий каталог для определенного драйва — GetDir.

CreateDir(const Dir: string): Boolean; — создает каталог. При этом предыдущий уровень должен присутствовать. Если вы хотите сразу создать всю вложенность каталогов используйте функцию ForceDirectories(Dir: string): Boolean; Обе функции возвращают True если каталог создан

DiskFree(Drive: Byte): Int64; — дает свободное место на диске. Параметер — номер диска 0 = текущий, 1 = A, 2 = B, и так далее

DiskSize(Drive: Byte): Int64; — размер винта. Обратите внимание на то что для результата этой и предыдущей функций абсолютно необходимо использовать переменную типа Int64, иначе макимум того что вы сможете прочитать правильно будет ограничен 2Gb

FileExists(const FileName: string) — применяется для проверки наличия файла

FileGetAttr(const FileName: string): Integer;
FileSetAttr(const FileName: string; Attr: Integer): Integer; — функции для работы с атрибутами файлов. Вот список возможных атрибутов:

RemoveDir(const Dir: string): Boolean; — удаляет папку(пустую)
DeleteFile(const FileName: string): Boolean; — удаляет файл
RenameFile(const OldName, NewName: string) — переименовывает файл

Информация о файле

Привожу пример функции которая собирает довольно большое количество информации о выбранном файле:

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

Теперь поговорим о поиске файлов. Для этой цели могут использоваться процедуры FindFirst, FindNext, FindClose, при участии переменной типа TSearchRec которая хранит информацию о текущем статусе поиска и характеристики последнего найденного файла.

Пример иллюстрирующий поиск всех файлов и каталогов в определенном каталоге:

Илон Маск рекомендует:  Пpеобpазование фypье
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL