Рассмотрим несколько практических примеров (везде далее f — файловая переменная). {262}
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 — текст-файловая переменная. {263}
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).
USES { Здесь используется ряд процедур из библиотеки } CRT; { модуля CRT. Они отмечены * в комментариях. } {Процедура считывает с клавиатуры значение типа Integer, помещая его в переменную V. При этом игнорируется любой ввод, не соответствующий этому типу. X и Y — координаты текста запроса Comment. Проверка корректности значений X и Y не производится. } PROCEDURE ReadInteger(X,Y: Byte; Comment: String; VAR V: Integer); |
Рис. 12.14 {264}
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 столбцов цифр, содержащий в некоторых строках словесные комментарии вме-{265}сто числовых значений. На рис. 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+}, используя его отключение только там, где действительно нужно обработать ошибку.