Дополнительные функции работы с файлами
|
Модуль |
|
|
ChDir |
Выполняет |
|
MkDir |
Создает |
|
RmDir |
Удаляет |
|
GetDir |
Получить |
|
Модуль |
|
|
DiskFree |
Число |
|
DiskSize |
Размер |
|
GetFAttr |
Получение |
|
SetFAttr |
Задание |
|
FSplit |
Получение |
|
FExpand |
Формирование |
|
FSearch |
Поиск |
|
FindFirst |
Поиск |
|
FindNext |
Поиск |
Прототипы
процедур FindFirst
и FindNext
имеют вид:
FindFirst(Path:
String; Attrib: Word; Var SR: SearchRec);
FindNext(Var
SR:
SearchRec);
Для
работы с этими подпрограммами требуются
следующие предопределенные описания:
-
Константы
Const
ReadOnly
= $01; только
для чтения
Hidden = $02; скрытый
SysFile =
$04; системный (непереносимый)
Volume ID = $08; метка
диска
Directory =
$10; подкаталог
Archive = $20; архивный
AnyFile = $3F; сумма
всех предыдущих
Эти атрибуты можно
складывать или вычитать (из anyfile).
-
Переменная
DOSError.
Она используется для анализа ошибок
MS
DOS.
Значение этой переменной, равное нулю,
соответствует отсутствию ошибки. Смысл
некоторых ненулевых кодов следующий:
2
– файл не найден
3
– маршрут не найден
5
– доступ к файлу запрещен
6
– неправильная обработка
8
– недостаточно памяти
10
– неверные установки значений параметров
среды
11
– неправильный формат
18
– файлов нет
При
работе процедуры FindFirst
возможны ошибки с номерами 2 и 18, а при
работе FindNext
– только 18.
3)
Тип
Type
SearchRec
= record
Fill: array[1..21] of byte; {системное поле}
attr: byte; {байт атрибутов}
time: longint; {время
создания}
size: longint; {размер
файла}
name: string[12]; {имя файла}
end;
Поля
переменной этого типа содержат информацию
о последнем файле, найденном с помощью
FindFirst
и FindNext.
Процедура
FindFirst
при заданных имени файла и атрибутах
должна вызываться лишь один раз. Она
записывает в поля переданной ей переменной
SR
типа SearchRec
информацию о первом найденном файле,
удовлетворяющем заданным условиям. Эта
информация в дальнейшем будет
использоваться процедурой FindNext,
которая всегда вызывается после FindFirst
и заполняет поля переменной SR
информацией о следующем найденном
файле.
Контроль
работы этих процедур ведется с помощью
переменной DOSError:
если файла нет, то DOSError<>0.
Обработка ошибок ввода-вывода
Все рассмотренные
ранее программы были написаны так, что
возможные ошибки ввода-вывода
игнорировались. Однако реально это не
так. В ТР стандартной реакцией на наличие
ошибок в вашей программе является
следующее: ваша программа аварийно
завершается и выдает сообщение:
Runtime
error
<номер> <смещение>.
Такое сообщение
буде полезно вам как программистам при
поиске места и причины ошибки в программе.
Однако показывать такое сообщение
пользователю вашей программы будет не
совсем дружественно.
Для того, чтобы
ваша программа из-за ошибки аварийно
не завершалась и для того, чтобы не
пугать пользователей непонятными им
сообщениями, вам необходимо перехватить
инициативу по обработке ошибок у системы
и писать собственные обработчики ошибок
стадии выполнения (исключений). Традиционно
обработка ошибок в программах
осуществляется с помощью установки
флагов (в
случае ошибки) и последующего анализа
этих флагов. Для перехвата ошибок
ввода-вывода на Паскале необходимо:
-
Специальным
образом оформить каждый фрагмент
программы, которые потенциально могут
вызвать ошибку. К таким фрагментам
относятся — открытие файда, чтение из
файла, запись в файл. -
При наличии такого
специального оформления необходимо
по завершению фрагмента проверить:
была ошибка или нет. Если была, то вы
должны выполнить ее обработку (хотя бы
вывести свое сообщение).
Пример:
reset(f);
при попытки открытия
файла физически файл может отсутствовать
на диске. Этот оператор в программе
является потенциальным источником
ошибки.
Специальное
оформление потенциально опасных участков
программы состоит в том, что
вы должны
как бы завернуть каждый опасный участок
в специальный код: выше
и ниже опасного фрагмента программы
вы должны
расположить две директивы компилятора:
{$I-}
— указывает на то, что необходимо
отключить системный контроль ошибок
reset(f);
{$I+}
— указывает на то, что необходимо включить
системный контроль ошибок
При наличии
директивы компилятора {$I-}
даже при наличии ошибок в/в программа
аварийно не завершается и ход выполнения
программы не нарушается.
Замечание:
По умолчанию действует директива {$i+}.
Контроль ошибок
в/в производится с помощью функции
IOResult
. Если ошибка имеет место, то значение
этой функции отлично от нуля. Поэтому
типичный шаблон обработки ошибки
ввода-вывода имеет вид:
If
IOResult
<> 0 Then
{обработка ошибки};
Обычно обработка
ошибки сводится к сообщению об ошибки
и завершению вашей программы (с помощью
процедуры HALT
или EXIT).
Замечание:
Опросить функцию IOResult
можно лишь
один раз
после каждой операции ввода-вывода,
т.к. она обнуляет свое значение перед
каждым вызовом. Поэтому целесообразно
сохранять значение, возвращенное
IOResult,
в специально выделенной переменной.
Пример.
{$I-}
reset(f);
{$I+}
If
IOResult <> 0
then
begin
Writeln(‘Ошибка
открытия
файла’);
Halt;
end;
Соседние файлы в папке WORD
- #
15.04.2015439.06 Кб286.docx
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
Рассмотрим несколько практических примеров (везде далее f — файловая переменная).
— 263 —
1. Обработка отсутствия файла с данными. Если файл отсутствует, то действие процедуры открытия Reset вызовет ошибку (рис. 12.10 ).
| Assign( f, ‘NoFile.TXT’ );
| {$I-} { выключение проверки ввода-вывода }
| Reset( f ); { попытка открыть файл f }
| {$I+} { восстановление проверки }
| if IOResult<>0 { Если файл не может быть открыт, }
| then { то дать сообщение: }
| WriteLn( ‘Файл не найден или не читается’ )
| else begin { Иначе (код равен 0) все хорошо }
| Read( f, … ); { и можно нормально работать с }
| … { файлом f… }
| Close(f)
| end; {else и if}
Рис. 12.10
В случае неудачи при открытии файла к нему не надо применять процедуру закрытия Close.
По тому же принципу можно построить функцию анализа существования файла (рис. 12.11).
| FUNCTION FileExists( FileName : String ) : Boolean;
| VAR
| f : File; { тип файла не важен }
| BEGIN
| Assign( f, FileName ); { связывание файла f }
| {$I-} Reset( f ); {$I+} { открытие без контроля }
| if IOResult=0 { Если файл существует, }
| then begin { то его надо закрыть }
| Close{ f );
| FileExists := True end {then}
| else { иначе просто дать знать}
| FileExists := False;
| END;
Рис. 12.11
2. Выбор режима дозаписи в текстовый файл или его создания. Механизм остается тот же (рис. 12.12). Здесь f — текст-файловая переменная.
— 264 —
| Assign(f,’XFile.TXT’); {связывание файла f }
| {$I-} Append( f ); {$I+} {попытка открыть его для дозаписи}
| if IOResult<>0 {Если файл не может быть открыт, }
| then Rewrite( f ); {то создать его. }
| …
| Write( f, …); { нормальная работа с файлом }
| …
| Close( f );
Рис. 12.12
3. Переход в заданный каталог или его создание, если переход возможен (рис. 12.13, S — строковая переменная).
| S := ‘C:NEWDIR’; { задано имя каталога }
| {$I-} ChDir( S ); {$I+} { попытка перейти в него }
| if IOResult<>0 { Если не получается, }
| then begin
| MkDir( S ); {то сначала создать его, }
| ChDir( S ) { а уж потом перейти. }
| end; {if}
| { Подразумевается, что каталог S в принципе создаваем. }
Рис. 12.13
4. Построение «умных» ждущих процедур чтения данных с клавиатуры. Такие процедуры не будут реагировать на данные не своего формата (рис. 12.14).
| { Здесь используется ряд процедур из библиотеки }
| CRT; { модуля CRT. Они отмечены * в комментариях. }
{Процедура считывает с клавиатуры значение типа Integer, помещая его в переменную V. При этом игнорируется любой ввод, не соответствующий этому типу. X и Y — координаты текста запроса Comment. Проверка корректности значений X и Y не производится. }
PROCEDURE ReadInteger( X,Y : Byte; Comment : String;
| VAR V : Integer );
Рис. 12.14
— 265 —
| CONST
| zone =12; { ширина окна зоны ввода числа }
| VAR
| WN.WX : Word; {переменные для хранения размеров окна }
| BEGIN
| WN:=WindMin; WX:=WindMax; {Сохранение текущего окна }
| {$I-} { отключение режима проверки }
| GotoXY( X,Y ); {*перевод курсора в X,Y }
| Write( Comment ); { печать комментария ввода }
| Inc(X, Length(Comment)); { увеличение координаты X }
| Window( X,Y, X+zone,Y ); {*определение окна на экране }
| Repeat { Главный цикл ввода числа: }
| ClrScr; {* очистка окна ввода, }
| ReadLn( V ); { считывание значения при $I- }
| until (IOResult=0); { пока не введено целое }
| {$I+} { включение режима проверки }
| {*восстановление окна: }
| Window( Lo(WN)+1, Hi(WN)+1, Lo(WX)+1, Hi(WX)+1 )
| END; {proc}
| VAR i : Integer; { === ПРИМЕР ВЫЗОВА ПРОЦЕДУРЫ === }
| BEGIN
| ClrScr; {* очистка экрана }
| ReadInteger(10,10,’Введите целое число: ‘,i); { вызов }
| WriteLn; WriteLn( ‘Введено i=’, i ); { контроль }
| ReadLn { пауза до нажатия ввода}
| END.
Рис 12.14 (окончание)
В примере можно попутно устроить проверку диапазона значений V, переписав условие окончания цикла в виде
until (IOResult=0) and (V<Vmax) and (V>Vmin);
где Vmax и Vmin — границы воспринимаемых значений V. Аналогичным способом, меняя лишь типы переменной V, можно определить процедуры ReadByte, ReadWord, ReadReal и т.п. Справедливости ради надо отметить, что хотя описанная процедура ReadInteger спокойно относится к попыткам впихнуть в нее буквы, дроби и прочие неподходящие символы, она чувствительна к превышению диапазона значений типа Integer во входном числе и не обрабатывает его.
5. Работа с текстовыми файлами данных произвольного формата. Пусть существует файл из N столбцов цифр, содержащий в некоторых строках словесные комментарии вме-
— 266 —
сто числовых значений. На рис. 12.15 показано, как можно прочитать из файла все цифровые данные, игнорируя строки-комментарии, текстовые строки или строки пробелов (а так же пустые).
| CONST N=3; { пусть в файле данные даны в трех столбцах }
| VAR
| f : Text; { текст-файловая переменная }
| i : Byte; { счетчик }
| D : Array [1..N] of Real; { значения одной строки }
| { данных в формате Real }
| BEGIN
| Assign(f,’EXAMPLE.DAT’); { связывание файла f }
| Reset( f ); { открытие файла для чтения }
| {$I-} { отключение режима проверки }
| while not SeekEOF(f) do { Цикл до конца файла: }
| begin
| Read( f, D[1] ); { попытка считать 1-е число }
| if IOResult=0 { Если это удалось,то затем }
| then begin { читаются остальные числа: }
| for i:=2 to N do Read( f, D[i] );
| { и как-либо обрабатываются: }
| WriteLn( D[1]:9:2, D[2]:9:2, D[3]:9:2 )
| end; {if 10…}
| ReadLn( f ) { переход на следующую строку }
| end; {while} { конец основного цикла }
| {$I+} { включение режима проверки }
| Close( f ); { закрытие файла f }
| ReadLn { пауза до нажатия ввода }
| END.
Рис. 12.15
По тому же принципу можно построить обработку ошибок позиционирования при прямом доступе в файлы и прочих задач, связанных с вводом-выводом.
Обращаем внимание на то, что во всех примерах подразумевается общий режим компиляции {$I+}, который в них всегда восстанавливается после завершения операции ввода-вывода. Советуем компилировать программы и модули в режиме {$I+}, используя его отключение только там, где действительно нужно обработать ошибку.
7.4 Обработка текстовых файлов в языке Free Pascal
При работе с текстовыми файлами следует учесть следующее:
- Действие процедур reset, rewrite, close, rename, erase и функции eof аналогично их действию при работе с компонентными (типизированными) файлами.
- Процедуры seek, trunсate и функция filepos не работают с текстовыми файлами.
- Можно пользоваться процедурой открытия текстового файла append(f), где f — имя файловой переменной. Эта процедура служит для открытия файла в режиме дозаписи в конец файла. Она применима только к уже физически существующим файлам, открывает и готовит их для добавления информации в конец файла.
- Запись и чтение в текстовый файл осуществляются с помощью процедур write, writeln, read, readln следующей структуры:
read ( f, x1, x2, x3,…, xn );
read ( f, x );
readln ( f, x1, x2, x3,…, xn );
readln ( f, x );
write ( f, x1, x2, x3,…, xn );
write ( f, x );
writeln ( f, x1, x2, x3,…, xn );
writeln ( f, x );
В этих операторах f — файловая переменная. В операторах чтения (read, readln) x, x1, x2, x3,…, xn — переменные, в которые происходит чтение из файла. В операторах записи write, writeln x, x1, x2, x3,…, xn — переменные или константы, информация из которых записывается в файл.
Есть ряд особенностей при работе операторов write, writeln, read, readln с текстовыми файлами. Имена переменных могут быть целого, вещественного, символьного и строкового типа. Перед записью данных в текстовый файл с помощью процедуры write происходит их преобразование в тип string. Действие оператора writeln отличается тем, что после указанных переменных и констант в файл записывается символ «конец строки».
При чтении данных из текстового файла с помощью процедур read, readln происходит преобразование из строкового типа к нужному типу данных. Если преобразование невозможно, то генерируется код ошибки, значение которого можно узнать, обратившись к функции IOResult. Компилятор Free Pascal позволяет генерировать код программы в двух режимах: с проверкой корректности ввода-вывода и без неё.
В программу может быть включен ключ режима компиляции. Кроме того, предусмотрен перевод контроля ошибок ввода-вывода из одного состояния в другое:
- {$I+} — режим проверки ошибок ввода-вывода включён;
- {$I-} — режим проверки ошибок ввода-вывода отключён.
По умолчанию, как правило, действует режим {$I+}. Можно многократно включать и выключать режимы, создавая области с контролем ввода и без него. Все ключи компиляции описаны в приложении.
При включённом режиме проверки любая ошибка ввода-вывода будет фатальной, программа прервётся, выдав номер ошибки.
Если убрать режим проверки, то при возникновении ошибки ввода-вывода программа не будет останавливаться, а продолжит работу со следующего оператора. Результат операции ввода-вывода будет неопределён.
Для опроса кода ошибки лучше пользоваться специальной функцией IOResult, но необходимо помнить, что опросить её можно только один раз после каждой операции ввода или вывода: она обнуляет своё значение при каждом вызове. IOResult возвращает целое число, соответствующее коду последней ошибки ввода-вывода. Если IOResult=0, то при вводе-выводе ошибок не было, иначе IOResult возвращает код ошибки. Некоторые коды ошибок приведены в табл. 7.9.
| Код ошибки | Описание |
|---|---|
| 2 | файл не найден |
| 3 | путь не найден |
| 4 | слишком много открытых файлов |
| 5 | отказано в доступе |
| 12 | неверный режим доступа |
| 15 | неправильный номер диска |
| 16 | нельзя удалять текущую директорию |
| 100 | ошибка при чтении с диска |
| 101 | ошибка при записи на диск |
| 102 | не применена процедура AssignFile |
| 103 | файл не открыт |
| 104 | файл не открыт для ввода |
| 105 | файл не открыт для вывода |
| 106 | неверный номер |
| 150 | диск защищён от записи |
Рассмотрим несколько практических примеров обработки ошибок ввода-вывода:
- При открытии проверить, существует ли заданный файл и возможно ли чтение данных из него.
assign ( f, ’ abc. dat ’ ); {$I-} reset ( f ); {$I+} if IOResult<>0 then writeln ( ’файл не найден или не читается ’ ) else begin read ( f,... ); close ( f ); end; - Проверить, является ли вводимое с клавиатуры число целым.
var i : integer; begin {$I-} repeat write ( ’введите целое число i ’ ); readln ( i ); until ( IOResult =0); {$I+} {Этот цикл повторяется до тех пор, пока не будет введено целое число.} end.
При работе с текстовым файлом необходимо помнить специальные правила чтения значений переменных:
- Когда вводятся числовые значения, два числа считаются разделёнными, если между ними есть хотя бы один пробел, или символ табуляции, или символ конца строки.
- При вводе строк начало текущей строки идёт сразу за последним, введённым до этого символом. Вводится количество символов, равное объявленной длине строки. Если при чтении встретился символ «конец строки», то работа с этой строкой заканчивается. Сам символ конца строки является разделителем и в переменную никогда не считывается.
- Процедура readln считывает значения текущей строки файла, курсор переводится в новую строку файла, и дальнейший ввод осуществляется с неё.
В качестве примера работы с текстовыми файлами рассмотрим следующую задачу.
ЗАДАЧА 7.8. В текстовом файле abc.txt находятся матрицы
и
и их размеры. Найти матрицу
, которую дописать в файл abc.txt.
Сначала создадим текстовый файл abc.txt следующей структуры: в первой строке через пробел хранятся размеры матрицы (числа N и M), затем построчно хранятся матрицы A и B.

Рис.
7.14.
Файл abc.txt
На рис. 7.14 приведён пример файла abc.txt, в котором хранятся матрицы A(4,5) и B(4,5).
Текст консольного приложения решения задачи 7.8 с комментариями приведён ниже.
program Project1;
{$mode objfpc}{$H+}
uses
Classes, SysUtils
{ you can add units after this };
var
f : Text;
i, j, N,M: word;
a, b, c : array [ 1.. 1000, 1.. 1000 ] of real;
begin
//Связываем файловую переменную f с файлом на диске.
AssignFile ( f, ’ abc. t x t ’ );
//Открываем файл в режиме чтения.
Reset ( f );
//Считываем из первой строки файла abc.txt значения N и M.
Read( f,N,M);
//Последовательно считываем элементы матрицы А из файла.
for i :=1 to N do
for j :=1 to M do
read ( f, a [ i, j ] );
//Последовательно считываем элементы матрицы B из файла.
for i :=1 to N do
for j :=1 to M do
read ( f, b [ i, j ] );
//Формируем матрицу C=A+B.
for i :=1 to N do
for j :=1 to M do
c [ i, j ] : = a [ i, j ]+b [ i ] [ j ];
//Закрываем файл f.
CloseFile ( f );
//Открываем файл в режиме дозаписи.
Append( f );
//Дозапись матрицы C в файл.
for i :=1 to N do
begin
for j :=1 to M do
//Дописываем в файл очередной элемент матрицы и пробел в
//текстовый файл.
write ( f, c [ i, j ] : 1 : 2, ’ ’ );
//По окончании вывода строки матрицы переходим на новую
//строку в текстовом файле.
writeln ( f );
end;
//Закрываем файл.
CloseFile ( f );
end.
После работы программы файл abc.txt будет примерно таким, как показано на рис. 7.15.

Рис.
7.15.
Файл abc.txt после дозаписи матрицы C