Воскресенье, 22.12.2024, 20:48 | Приветствую Вас Гость

Information inovation!

Главная » Статьи » Компы! » Програмирование!

Работа с файлами Паскаля
Поводом для написания этих уроков послужила дискуссия на сайте www.delphimaster.ru об организации уроков для начинающих.

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

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

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

Это было интересное время, все начинали с нуля, знаний и опыта у всех было мало, все учились друг у друга, в тоже время начал развиваться Интернет, что позволило эффективно обмениваться знаниями. Не было обширного парка компонент, только потом развились файловые архивы уровня www.torry.net и Delphi Super Pages. Все это позволило наработать большой опыт и пришло время отдавать долги, что и делаю в виде серии этих статей для начинающих.

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

Анатолий Подгорецкий

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

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

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

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

Текстовые файлы – рассматривается простая работа с текстовыми файлами;
Типизированные файлы – то же самое, но для типизированных файлов;
Нетипизированные файлы – немного будет рассмотрена работа и с нетипизированными файлами, основные моменты, но вместо этого лучше использовать класс TFileStream, работа с которым и проще, а возможности шире;
Расширенная работа с тестовыми файлами, в данной главе будут рассмотрены более сложные методы работы, работа со строкой не как с целой строкой, а как с набором различных типов, Паскаль поддерживает автоматическое преобразование типов в текстовый формат и обратно, производя разбор строки при чтении и ее формирование при записи;
Использование текстовых файлов для импорта/экспорта, рассмотрим импорт в Эксель.
Работа с файлами Паскаля едина для трех основных типов файлов и очень простая. Ведется она через файловую переменную, одного из трех типов, к которой применяются функции и процедуры.

Типовая последовательность следующая:

Объявляется файловая переменная нужного типа;
С этой файловой переменной связывается файл, функцией AssignFile;
Затем файл открывается Reset/Rewrite/;
Производятся операции чтения или записи, разновидности Read/Write;
Файл закрывается CloseFile.
Примечание:

Статья написана специально для Королевства Дельфи, как эксклюзивный материал, использование данной статьи на других сайтах разрешено только по получению особого разрешения от Королевства Дельфи www.delphikingdom.com

Текстовые файлы
Для начала разберемся, что такое текстовые файлы и в чем их различие от двоичных файлов. Текстовые файлы являются подмножеством двоичных файлов, но в отличии от двоичных не могут содержать весь набор символов. Вся информация в файле разбивается на строки, ограниченные символам возврат каретки (CR) и перевод строки (LF). Допустимые символы это символы с кодами от 32 до 255, символы с кодами ниже 32 являются управляющими и допустимы только следующие коды:

08 (BS) - возврат на шаг
09 (TAB) - табуляция
0A (LF) - перевод строки
0C (FF) – перевод листа
0D (CR) – возврат каретки
1A (EOF) – конец файла

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

Паскаль поддерживает работу с такими файлами, через файловую переменную типа TextFile, где основной единицей является строка, состоящие из основных базовых типов (в текстовом виде, разделенные пробелом), наиболее часто это просто строка, как набор символов. В качестве примера напишем программу преобразования из DOS кодировки (OEM) в Windows (ANSI). Техническое задание:

Программа должна работать в консольном режиме и получать входные параметры через командную строку.
Программа называется Oem2Ansi
На вход поступают два параметра, имя исходного файла и имя выходного файла;
Имя выходного файла может быть опущено, в этом случае используется имя входного файла, с изменением расширения выходного на .ANS;
Если имена не указаны, то должна выводиться справка о синтаксисе команды;
ошибки обрабатывать будем в минимальном объеме, коды ошибок выдаются как ErrorLevel и доступны для обработки в .bat файле.
Текст программы Oem2Ansi
program Oem2Ansi;

{$APPTYPE CONSOLE}

uses
Windows,
SysUtils;
var
InFile: TextFile;
OutFile: TextFile;
InFilename: string;
OutFilename: string;
S: string;
begin
if ParamCount = 0
then
begin
WriteLn('Syntax is: Oem2Ansi Infile [outfile]');
Halt(1); // Ошибка синтаксиса
end;
InFilename := ParamStr(1);
if ParamCount = 2
then
OutFileName := ParamStr(1)
else
OutFileName := ChangeFileExt(InFilename, '.ans');
AssignFile(InFile, InFilename); // связываем входной файл
AssignFile(OutFile, OutFilename); // и выходной выходной файл
try
try
Reset(InFile); // открываем входной файл
Rewrite(OutFile); // создаем выходной файл
while not EOF(InFile) do // крутим пока не конец файла
begin
Readln(Infile, S); // читаем строку
if Length(S) > 0 // на вход функции можно
then // подавать только не пустые строки
begin
OemToChar(Pchar(S), Pchar(S));
WriteLn(Outfile, S); // записываем строку
end
else
begin
WriteLn(Outfile); // записываем пустую строку
end;
end;
except
Halt(2); // любая ошибка
// не удалось преобразовать файлы
end;
finally
CloseFile(InFile);
CloseFile(OutFile);
end;
end.

Разберем работу программы по кусочкам. Вначале объявляются две файловые переменные текстового типа и две переменные для имен файлов, а также одна переменная для хранения и обработки строки. Затем анализируются входные параметры, если параметры не указываются, то выводится сообщение об ошибки и программа заканчивается с кодом выхода 1. Обратите внимание на форму процедуру WriteLn, если в параметрах не указывается файловая переменная, то вывод производится на консоль, что удобно для выдачи различных сообщений, данная форма возможна только для консольного приложения и не применима в GUI приложении.

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

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

Первое действие состоит в открытии файлов, входной файл открывается с помощью процедуры Reset - это открытие текстового файла в режиме чтения, а второй с помощью Rewrite – открытие в режиме записи, если файл существует то он переписывается. Есть еще одна форма открытия текстовых файлов, это функция Append(FileVar), открытие в режиме добавления строк, если файл существует, то курсор позиционируется в конец файла и файл открывается в режиме записи, если файла нет, то он создается. После нее управление передается в блок finally. В случае ошибки управление сначала передается в блок except, а затем в блок finally.

После этого создается цикл чтения сток пока не будет, достигнут конец файла, или физический или будет встречен символ EOF. Функция EOF(FileVar).

Внутри цикла читается строка во временную переменную Readln(Infile, S) и тут принята одна предосторожность, в функцию Oem2Char НЕЛЬЗЯ передавать пустые строки, поэтому производится анализ длины строки, если строка не нулевая, то производится конвертирования и запись ее в выходной файл, процедурой WriteLn(Outfile, S), иначе в файл пишется пустая строка.

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

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

Для особо желающих сделать GUI версию, с диалогами выбора файлов, с прогресс-бар, с предварительным просмотром первых 10-20 строк входного файла (переключение кнопкой OEM/ANSI), с целью определения направления перекодирования, с остальными наворотами, которые сумеет придумать душа, например пакетная обработка всех файлов из папки.

Типизированные файлы
Второй тип файлов, для которого нет поддержки в OS и VCL – это типизированные файлы. Это такой вид файлов, в котором содержатся записи одного типа и фиксированной длины. Часто используется или для организации мини баз, конфигураций, иногда для импорта/экспорта в специальных форматах. Работа с такими файлами не сложнее, чем работа с текстовыми файлами, наряду с освоенными методами добавляется только одно новое свойство. Если текстовые файлы чисто последовательные, то в типизированных файлах можно перемещаться на любую запись и затем производить последовательное чтение или запись. Это очень похоже на работу с TFileStream за одним исключением, единицей информации является не байт, а тип.

Типизированный файл определяется следующим образом

var
FileVar: file of тип;

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

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

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

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

Для уменьшения длины файла можно использовать функцию Truncate, которая обрезает файл по текущей позиции

Замечания по поводу открытия файлов, для этого используются две ранее описанные процедуры: Rewrite - создает новый файл для чтения/записи, если такой файл существует, то его длина устанавливается в ноли и Reset - открывает файл для чтения/записи и не изменяет его длины. Сразу видно различие в этих процедурах по отношению к текстовым файлам.

Примечание: записи считаются с нуля

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

type
TPhoneRec = packed record
PersonName: string[25];
Address: string[25];
Phone: string[16];
end;

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

Примеры работы с типизированными файлами
// опредение глобальных для модуля или программы переменных
var
PersonFile: file of TPhoneRec; // определили файл нашего типа
DbOpen: Boolean; // флаг состояния базы
PhonesCount: Integer // количество записей в базе

// открытие базы и нициализация
// не пытаться работать с базой если результат = FALSE
function OpenDB(const DbName: string): Boolean;
begin
AssignFile(PersonFile, DbName);
try
Reset(PersonFile); // открываем базу
PhonesCount := FileSize(PersonFile); // текущее количество записей
DbOpen := TRUE; // открытипе прошло нормально
except
PhonesCount := 0;
DbOpen := FALSE;
end;
Result := DbOpen;
end;

// создание новой базы и нициализация
// не пытаться работать с базой если результат = FALSE

function CreateDB(const DbName: string): Boolean;
begin
AssignFile(PersonFile, DbName);
try
Rewrite(PersonFile); // открываем базу
DbOpen := TRUE; // открытипе прошло нормально
except
DbOpen := FALSE;
end;
PhonesCount := 0; // записей нет
Result := DbOpen;
end;

// закрытие базы
function CloseDB: Boolean;
begin
if DbOpen
then
CloseFile(PersonFile);
end;

// Удвлить все после указанной записи
function TruncateDB(const RecNo: Integer);
begin
Seek(PersonFile, RecNo);
Truncate(PersonFile);
end;

// Читать следующую, за указателем запись
function ReadNextRec: TPersonRec;
begin
Read(PersonFile, Result);
end;

// Читать указанную запись
function ReadRec(const RecNo: Integer): TPersonRec;
begin
Seek(PersonFile, RecNo);
Result := ReadNextRec;
end;

// изменить следующую, за указателем запись
procedure ModifyNextRec(const RecNo: Integer; const Rec: TPersonRec);
begin
Write(PersonFile, Rec);
end;

// изменить указанную запись
procedure ModifyRec(const RecNo: Integer: const Rec: TPersonRec);
begin
Seek(PersonFile, RecNo);
ModifyNextRec(Rec);
end;

// Добавить новую запись в конец файла
procedure AddRec(const Rec: TPersonRec);
begin
Seek(PersonFile, PhonesCount); // переместиться на последнею запись
ModifyNextRec(Rec); // и добавить запись
PhonesCount := FileSize(PersonFile); // новое количество записей
end;

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

program ReadPhoneBook;

{$APPTYPE CONSOLE}

uses
PhoneDb;
var
PhoneBook: array of TPhoneRec; // телефонный справояник
I: Integer;
begin
if OpenDB('C:\DB\Phones.dbf')
then
begin
SetLength(PhoneBook, PhonesCount);
for I := 0 to PhonesCount - 1 do
begin
PhoneBook[I] := ReadNextRec;
end;
end;
end.

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

2-страница

Категория: Програмирование! | Добавил: Prise (04.06.2007)
Просмотров: 12834 | Комментарии: 1 | Рейтинг: 3.0/2
Всего комментариев: 0
Имя *:
Email *:
Код *:
Наш опрос
Чего не хватает на сайте
Всего ответов: 42
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Поиск
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz