��������� ����������
��������� ����������: �����
����� �� ����� ���������� ��������� ���������� ������, ������������ ��� ���������� ����������, ������� ����� ����������� � ����������. ���������� ������������ ����� ������ ������, ������������ �� ������ Exception, ����������� ��� ������������� �������������� ��������.
������� ��� ����������� ����� ����������. ����� ����� ���������� ���������������� ���� ����������.
���� ���������� �� ����������, �� ��������� ���������� � �������. ��� ��������� ���������� ������������ �������� try … except.
������ ���������� ������������ � �������������, ��������� ����������� ������������, ��� �������, �� �����, ��� ���������� ��������� ��������. � ����� ������ ������������ ���, ��� �������, ��������, ����� ������� ������� ������������ ����������. ��������, ����� ����������� ��������� �������:
function mymod(a,b: integer): integer;
begin
Result := a — (a div b) * b;
end;
���� ������� mymod(1,0), �� ����� ���������� ���������� System.DivideByZeroException �������������� ������� �� 0.
���������� ������� ������� ���������� ��������� �������� ������ ������� mymod:
function mymod(a,b: integer): integer;
begin
if b = 0 then
writeln(‘������� mymod: ������� �� 0’);
Result := a — (a div b) * b;
end;
�������� ������� �������� ������, ��������� �����������, ��������������� ������� mymod, �� �����, ��� ��� ����� ��������������. ��������, ��� ������ ������� mymod � ����� �� ������ �� ������ ������������ ��������� �� ������.
���������� ������ — �������� �������� ������� ������� � ������������ ���������� System.DivideByZeroException:
try
readln(a,b);
writeln(mymod(a,b) mod (a-1));
…
except
on System.DivideByZeroException do
writeln(‘������� �� 0’);
end;
������� �� ������ ������ ������� ������� � ���, ��� ��� ���������� ��������� �� ���� ���������� ��������, ������� ���������� ��������� ��� ��������� ����������. ��� ����� ���� ������������� ��������� �� ������, ����� � ���� ������ ��� ������ �������� (� ������, ����� ��������� ��������� �������� ����������).
������, ������ ������� �������� ������������ �����������: ���������� System.DivideByZeroException ����� ���������� � ��� a=1 � �� ����� ������� � �������� mymod. ��� ���������� ��������� ���������� ��������� ����������� ����� ���������� � �������� ��� � ������� mymod:
type MyModErrorException = class(System.Exception) end;
function mymod(a,b: integer): integer;
begin
if b = 0 then
raise new MyModErrorException(‘������� mymod: ������� �� 0’);
Result := a — (a div b) * b;
end;
����� ��������� ������ ����� ��������� ���:
try
readln(a,b);
writeln(mymod(a,b) mod (a-1));
…
except
on System.DivideByZeroException do
writeln(‘������� �� 0’);
on e: MyModErrorException do
writeln(e.Message);
else writeln(‘�����-�� ������ ����������’)
end;
���� ������� MyModErrorException ����������� ������ System.ArithmeticException, ��� � System.DivideByZeroException, �� ��������� ��� ����� ���������:
type MyModErrorException = class(System.ArithmeticException) end;
…
try
readln(a,b);
writeln(mymod(a,b) mod (a-1));
…
except
on e: System.ArithmeticException do
writeln(e.Message);
else writeln(‘�����-�� ������ ����������’)
end;
�������, ����� ��������� ��������� �������. ���������� � ������� mymod ���������� System.DivideByZeroException � � ����� ����������� ����� — MyModErrorException:
function mymod(a,b: integer): integer;
begin
try
Result := a — (a div b) * b;
except
on e: System.DivideByZeroException do
raisenew MyModErrorException(‘������� mymod: ������� �� 0’);
end;
end;
Оператор try … except
Оператор try
… except
имеет вид:
try
операторы
except
блок обработки исключений
end;
Блок try
называется
защищаемым блоком.
Если при выполнении программы в нем происходит ошибка, то он завершается и
выполнение передается блоку except
. Если исключение
обрабатывается в блоке except
, то после его обработки
программа продолжает выполняться с оператора, следующего за try
…
except
… end
. Если исключение остается необработанным и имеется объемлющий
блок try
, то выполнение передается
его блоку except
. Если объемлющего
блока try
нет, то программа
завершается с ошибкой. Наконец, если в блоке try
ошибки не произошло, то блок except
игнорируется и выполнение программы продолжается дальше.
Если в процессе обработки исключения (в блоке
) произошло другое исключение, то текущий
except
блок except
завершается, первое
исключение считается необработанным и обработка нового исключения передается
объемлющему блоку try
. Таким образом,
в каждый момент времени существует максимум одно необработанное исключение.
Блок обработки
исключений представляет собой либо последовательность
операторов, разделенных точкой с запятой, либо последовательность обработчиков
исключений вида
on
имя:
типdo
оператор
Обработчики разделяются символом ‘;
‘,
после последнего обработчика также может следовать символ ‘;
‘.
Здесь тип — тип исключения
(должен быть производным от стандартного типа Exception
),
имя — имя переменной исключения (имя с последующим двоеточием может быть
опущено). В первом случае при обработке исключения выполняются все операторы из
блока except
. Во втором случае среди
обработчиков осуществляется поиск типа текущего исключения (обработчики
перебираются последовательно от первого до последнего), и если обработчик
найден, то выполняется соответствующий оператор обработки исключения, в
противном случае исключение считается необработанным и передается объемлющему
блоку try
. В последнем случае после всех
обработчиков on
может идти ветвь else
,
которая обязательно обработает исключение, если ни один из обработчиков не
выполнился.
Следует обратить внимание, что имя переменной исключения в
разных обработчиках может быть одинаковым, т.е. оно локально по отношению к
обработчику.
Поиск типа исключения в обработчиках производится с учетом
наследования: исключение будет обработано, если оно принадлежит к указанному в
обработчике типу или производному от него. Поэтому принято записывать вначале
обработчики производных классов, а затем — обработчики базовых (в противном
случае обработчик исключения производного класса никогда не сработает).
Обработчик исключения Exception
обрабатывает
все возможные исключения и поэтому должен быть записан последним.
Пример.
var a: array [1..10] of integer;
try
var i: integer;
readln(i);
writeln(a[i] div i);
...
except
on System.DivideByZeroException do
writeln('Деление на 0');
on e: System.IndexOutOfRangeException do
writeln(e.Message);
on System.FormatException do
writeln('Неверный формат ввода');
else writeln('Какое-то другое
исключение');
end;
Оператор
try
… except
имеет вид:
try
операторы
except
блок обработки исключений
end;
Блок
try
называется защищаемым блоком. Если
при выполнении программы в нем происходит
ошибка, то он завершается и выполнение
передается блоку except.
Если исключение обрабатывается в блоке
except,
то после его обработки программа
продолжает выполняться с оператора,
следующего за try
… except
… end.
Если исключение остается необработанным
и имеется объемлющий блок try,
то выполнение передается его блоку
except.
Если объемлющего блока try
нет, то программа завершается с ошибкой.
Наконец, если в блоке try ошибки не
произошло, то блок except
игнорируется и выполнение программы
продолжается дальше.
Если
в процессе обработки исключения (в блоке
except)
произошло другое исключение, то текущий
блок except
завершается, первое исключение остается
необработанным и обработка нового
исключения передается объемлющему
блоку try.
Таким образом, в каждый момент времени
существует максимум одно необработанное
исключение.
Блок
обработки исключений представляет
собой либо последовательность операторов,
разделенных точкой с запятой, либо
последовательность обработчиков
исключений вида
on
имя:
тип do
оператор;
где
тип — тип исключения (должен быть
производным от типа Exception),
имя — имя переменной исключения (имя
с последующим двоеточием может быть
опущено). В первом случае при обработке
исключения выполняются все операторы
из блока except.
Во втором случае среди обработчиков
осуществляется поиск типа текущего
исключения, и если обработчик найден,
то выполняется соответствующий оператор
обработки исключения, в противном случае
исключение считается необработанным
и передается объемлющему блоку try.
Следует
обратить внимание, что имя переменной
исключения в разных обработчиках может
быть одинаковым, т.е. оно локально по
отношению к обработчику.
Поиск
типа исключения в обработчиках
производится с учетом наследования:
исключение будет обработано, если оно
принадлежит к указанному в обработчике
типу или производному от него. Поэтому
принято записывать вначале обработчики
производных классов, а затем — обработчики
базовых (в противном случае обработчик
исключения производного класса никогда
не сработает). Обработчик исключения
Exception
обрабатывает все возможные исключения
и поэтому должен быть записан последним.
Пример.
try
readln(x);
writeln(ctg(x)/(x-1));
…
except
on
EZeroDivide do
writeln(‘Деление
на
0’);
on
e: ECtgError do
writeln(e.Message);
end;
Оператор try … Finally
Оператор
try
… finally
имеет вид:
try
операторы
finally
операторы
end;
Операторы
в блоке finally
выполняются безотносительно к тому,
возникло или нет исключение в блоке
try.
При этом само исключение не обрабатывается.
Блок
finally
используется для возвращения ранее
выделенных ресурсов.
Пример
1. Закрытие открытого файла.
reset(f);
try
…
finally
close(f);
end;
Файл
будет закрыт независимо от того, произошло
ли ислючение в блоке try.
Пример
2. Возвращение выделенной динамической
памяти.
New(p);
try
…
finally
Dispose(p);
end;
Динамическая
память, контролируемая указателем p,
будет возвращена независимо от того,
произошло ли ислючение в блоке try.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
Читайте также
ГЛАВА 4 Обработка исключений
ГЛАВА 4
Обработка исключений
Основное внимание в данной главе сфокусировано на структурной обработке исключений (Structured Exception Handling, SEH), но наряду с этим обсуждены также обработчики управляющих сигналов консоли и векторная обработка исключений (Vectored Exception Handling, VEH).SEH
Пример: обработка ошибок как исключений
Пример: обработка ошибок как исключений
В предыдущих примерах для обработки ошибок при выполнении системных вызовов и других ошибок используется функция ReportError. Эта функция прекращает выполнение процесса, если программист указал, что данная ошибка является
SEH и обработка исключений в C++
SEH и обработка исключений в C++
При обработке исключений в C++ используются ключевые слова catch и throw, а сам механизм исключений реализован с использованием SEH. Тем не менее, обработка исключений в C++ и SEH — это разные вещи. Их совместное применение требует внимательного
Векторная обработка исключений
Векторная обработка исключений
Функции обработки исключений можно непосредственно связывать с исключениями, точно так же, как обработчики управляющих сигналов консоли можно связывать с управляющими событиями консоли. В этом случае, если возникает исключение, то
13.1.5. Обработка исключений
13.1.5. Обработка исключений
Что произойдет, если в потоке возникнет исключение? Как выясняется, поведение можно сконфигурировать заранее.Существует флаг abort_on_exception, который работает как на уровне класса, так и на уровне экземпляра. Он реализован в виде метода доступа (то
Обработка исключений
Обработка исключений
Исключение (exception) — это результат выполнения некорректного оператора, что привело к возникновению ошибки. В языке Object Pascal для обработки исключений предназначена специальная конструкция:try //Операторы, которые могут привести к возникновению
Обработка исключений
Обработка исключений
Ввиду того, что теперь метод Accelerate() может генерировать исключение, вызывающая сторона должна быть готова обработать такое исключение. При вызове метода, способного генерировать исключение, вы должны использовать блок try/catch. Приняв исключение, вы
Обработка множеств исключений
Обработка множеств исключений
В простейшем варианте блок try имеет единственный блок catch. Но на практике часто возникает ситуация, когда операторы в рамках блока try способны создавать множество возможных исключений. Например, представьте себе, что метод Accelerate()
11. Обработка исключений
11. Обработка исключений
Обработка исключений – это механизм, позволяющий двум независимо разработанным программным компонентам взаимодействовать в аномальной ситуации, называемой исключением. В этой главе мы расскажем, как генерировать, или возбуждать, исключение в
Обработка исключений
Обработка исключений
Код PSQL может перехватывать ошибки при их появлении и затем их обрабатывать в подпрограмме обработки исключений. Если исключение будет обработано в вашем коде- вы обеспечите исправление или обход ошибки и позволите продолжить выполнение, — то
Обработка исключений
Обработка исключений
Ошибки в isql обрабатываются тем же образом, что и приложении DSQL. isql отображает сообщение об ошибке, содержащее переменную SQLCODE и текст сообщения из массива состояния Firebird, как показано на рис. 37.4.
Рис. 37.4. Пример сообщения об ошибке в isqlОшибки SQL со
Продвинутая обработка исключений
Продвинутая обработка исключений
Чрезвычайно простой механизм, разработанный до сих пор, удовлетворяет большинству потребностей обработки исключений. Но некоторые приложения могут требовать более тонкой настройки:[x]. Возможно, требуется определить природу последнего
Дополнительные функции работы с файлами
Модуль |
|
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.
Таблица
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