Обработка ошибок ввода-вывода

Компилятор Турбо Паскаля позволяет генерировать выполняемый код в двух режимах: с проверкой корректности ввода–вывода и без нее. При включении в программу ключ режима компиляции обозначается:

{$I+} – режим проверки включен;

{$I-} – режим отключен.

 

По умолчанию действует режим $I+. Этот ключ компиляции имеет локальную сферу влияния. Можно многократно включать и выключать режим, вставляя в текст программы конструкции {$I+} и {$I-}, тем самым создавая области с контролем ввода–вывода и без него. При включенном режиме проверки любая ошибка ввода–вывода будет фатальной: программа прервется, выдав номер ошибки. Если отключить режим проверки, то при возникновении ошибки ввода–вывода программа не прервется, а продолжит работу со следующего оператора. Функция IOResult : Integer возвращает целое число, соответствующее коду последней ошибки ввода–вывода. Если же операция ввода–вывода прошла без сбоев, то функция вернет значение 0.

Если файл на диске отсутствует, то действие процедуры открытия Reset вызовет ошибку

 

Assign (f, ‘file1’);

{$I-} Reset (f) {$I+};

If IOResult <> 0 Then Writeln(‘Файл не найден’)

Else . . .

 

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

 

Задача 3.Составить программу формирования базы данных “Склад”. Предусмотреть возможность просмотра, корректирования и дополнения базы данных. Каждая запись базы данных содержит следующие поля: наименование товара, дата поступления товара, цена единицы товара, количество.

В исполнительной части программы выведем на экран меню для выбора режима работы:

1 – Формирование новой базы данных.

2 – Просмотр базы данных.

3 – Корректирование записей.

4 – Дополнения в базу.

5 – Конец работы.

При корректировании записи производится ввод номера i изменяемой записи и перемещение указателя файла на запись с этим номером с помощью процедуры Seek(f, i). Для того, чтобы новые данные записать на место старой компоненты, указатель необходимо передвинуть на одну запись назад – Seek(f, i - 1). При добавлении записи в конец файла указатель файла устанавливается в конец файла – Seek(f, Filesize(f)).

Общая схема алгоритма:

 

Рис. 2. Укрупненная схема алгоритма к задаче 3

 

В программе введены следующие обозначения переменных:

Sklad – тип компоненты. Это записной тип, имеющий следующие поля: Name – наименование товара; Data – дата поступления товара; Price – цена товара; Count – количество.

FileName – имя файла.

code – код ошибки.

f – имя логического файла.

Sk – компонента файла.

 

Программа.

Program Task3;

Uses CRT;

Type

Tf = String[20];

Date = Record day : 1..31; Mon : 1..12; Year : 1..99; End;

Sklad = Record {Тип компоненты файла}

Name : Tf;

Data : Date;

Price : Real;

Count : Integer

End;

Var FileName : Tf; R : Byte;

{Формирование базы данных ——————————————————————}

Procedure Vvod(fname : Tf); {Входной параметр – имя файла}

Var f : File Of Sklad;{Объявили файловую переменную}

Sk : Sklad; {Переменная для компоненты файла}

Begin

Clrscr;

Assign (f, fname); {Связали файловую переменную с физическим

файлом на диске}

Rewrite(f); {Открыли файл для записи}

{Ввод 1-го компонента}

Write(‘Наименование товара ‘); Readln(Sk.Name);

Writeln(‘Признак окончания ввода - пусто для наименования товара.’);

While sk.Name <> ‘’ do

Begin

With Sk Do

Begin {Вводим значения полей записи в оперативную память}

Writeln(‘Дата поступления’);

Write(‘День ‘); Readln(Data.Day);

Write(‘Месяц ‘); Readln(Data.Mon);

Write(‘Год ‘); Readln(Data.Year);

Write(‘Цена за единицу товара ‘); Readln(Price);

Write(‘Количество в шт. ‘); Readln(Count);

End;

Write(f, Sk); {Записали компоненту файла на диск}

{Переходим к вводу следующей компоненты}

Write(‘Наименование товара ‘); Readln(Sk.Name);

End;

Writeln(‘Для продолжения нажмите <Enter>’);

Repeat Until keypressed;{Задержка экрана}

Close (f);

End;

{Просмотр базы данных————————————————————————}

Procedure Look (fname : Tf); {Параметр – имя файла}

Var f : File Of Sklad; Sk : Sklad; i, code : Integer;

Begin

Clrscr;

Assign (f, fname);

{Блок проверки наличия файла на диске}

{$i-} {Отключили директиву компилятора проверки корректности

ввода–вывода}

Reset(f);

{$i+} {Включили директиву компилятора проверки корректности

ввода–вывода}

code := IOResult;{Определили код последней ошибки ввода–вывода}

If code <> 0 Then Begin {Если ошибка возникла, то ее код не равен 0}

Writeln(‘Файла с таким именем не существует на диске!’);

Delay(2000); {Задержка экрана}

End;

i:=0;

While Not eof(f) do {Делать, пока не достигнут конец файла}

Begin

Read(f, sk); {Считать с диска компоненту файла}

With Sk Do {Вывод полей компоненты на экран}

Begin

i := i + 1;

Writeln(‘Запись номер ‘, i);

Writeln(‘Наименование товара ‘, Name);

Writeln(‘Дата поступления ‘, Data.Day, ‘/’, Data.Mon, ‘/’, Data.Year);

Writeln(‘Цена за единицу товара ‘, Price:9:2);

Writeln(‘Количество в шт. ‘, Count);

Delay(1000); {Небольшая задержка экрана после вывода

каждой записи}

End;

End;

Writeln(‘Для продолжения нажмите <Enter>’);

Repeat Until keypressed;

Close (f);

End;

{Корректировка базы данных——————————————————————}

Procedure Correct(fname : Tf);

Var f : File Of Sklad; i, code : Integer; Sk : Sklad;

Begin

Clrscr;

Assign (f, fname);

{Блок проверки наличия файла на диске}

{$i-}

Reset(f);

{$i+}

code := IOResult;

If code <> 0 Then Begin

Writeln(‘Файла с таким именем не существует на диске!’);

Delay(2000);

End;

i := Filesize(f); {Определили количество компонент файла}

Writeln(‘Файл содержит ‘, i ,’ записей.’);

Write(‘Какую будете корректировать? (0 - конец)’);

Readln(i); {Ввели номер компоненты, которую нужно корректировать}

While i > 0 Do {Корректируем до тех пор, пока не введем 0}

Begin

Seek(f, i - 1); {Установили указатель файла перед

корректируемой компонентой}

Read(f, Sk); {Cчитали c диска компоненту с номером i}

With Sk Do

Begin

{Выводим значения полей на экран и вводим новые значения}

Writeln(‘Запись номер ‘, i);

Write(‘Наименование товара’, Name,’ ?’); Readln(Name);

Writeln(‘Дата поступления ‘,Data.Day,’/’,Data.Mon,’/’,Data.Year);

Write(‘День ? ‘); Readln(Data.Day);

Write(‘Месяц ? ‘); Readln(Data.Mon);

Write(‘Год ? ‘); Readln(Data.Year);

Write(‘Цена за единицу товара ‘,Price : 9 : 2,’ ?’); Readln(Price);

Write(‘Количество в шт.’, Count,’ ?’); Readln(Count);

End;

Seek(f, i-1); {Установили указатель файла}

Write(f, Sk); {и записали новые значения полей компоненты}

Write(‘Какую будете корректировать? (0 - конец)’);

Readln(i); {Переход к следующей итерации цикла}

End;

Writeln(‘Для продолжения нажмите <Enter>’);

Repeat Until keypressed;

Close (f);

End;

{Дополнение базы данных———————————————————————}

Procedure AppenF(fname : Tf);

Var f : File Of Sklad; Sk : Sklad; code : Byte;

Begin

Clrscr;

Assign (f, fname);

{Блок проверки наличия файла на диске}

{$i-}

Reset(f);

{$i+}

code := IOResult;

If code <> 0 Then Begin

Writeln(‘Файла с таким именем не существует на диске!’);

Delay(2000); Exit;

End;

Seek(f, Filesize (f));

Writeln(‘Признак окончания ввода - пусто для наименования товара.’);

Write(‘Наименование товара ‘); Readln(Sk.Name);

While sk.Name <> ‘’ do

Begin

With Sk Do

Begin

Writeln(‘Дата поступления’);

Write(‘День ‘); Readln(Data.Day);

Write(‘Месяц ‘); Readln(Data.Mon);

Write(‘Год ‘); Readln(Data.Year);

Write(‘Цена за единицу товара ‘); Readln(Price);

Write(‘Количество в шт. ‘); Readln(Count);

End;

Write(f, Sk);

Write(‘Наименование товара ‘); Readln(Sk.Name);

End;

Writeln(‘Для продолжения нажмите <Enter>’);

Repeat Until keypressed;

Close(f);

End;

{Исполняемая часть —————————————————————————}

Begin

Clrscr;

{Меню для выбора режима работы}

Repeat

Write(‘Введите имя файла.’); Readln(FileName);

Clrscr;

Gotoxy(20,8); Write(‘1 - Формирование новой базы данных’);

Gotoxy(20,10); Write(‘2 - Просмотр базы данных’);

Gotoxy(20,12); Write(‘3 - Корректировка базы данных’);

Gotoxy(20,14); Write(‘4 - Добавление записи в конец базы данных’);

Gotoxy(20,16); Write(‘5 - Конец работы’);

Gotoxy(20, 18); Write(‘Режим? ‘);Readln(R);

Case R Of

1: Vvod (FileName); {Обращение к процедуре формирования базы}

2: Look (FileName); {Обращение к процедуре просмотра базы}

3: Correct (FileName); {Обращение к процедуре коррекции записей базы}

4: AppenF (FileName); {Обращение к процедуре добавления записи}

End;

Until R = 5;

End.

 

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

Процедура Erase (f)стирает файл f. Перед этим файл нужно закрыть с помощью процедуры Close. Процедура Rename (F, S)переименовывает файл F в S. В каталог вносится соответствующее изменение. Перед этой процедурой файл f должен быть закрыт с помощью Close.

 

Задача 4.Написать процедуру удаления записи базы данных Sklad, сформированной в предыдущей программе. Из базы данных удаляются все записи, которые соответствуют товарам с годом поступления < 95.

 

{Удаление записей базы данных}

Procedure DelF(fname : Tf);

Var f, f1 : File Of Sklad; Sk : Sklad; code : Byte; God : 1 .. 99;

{Ввели описание новой файловой переменной – f1}

Begin

Clrscr;

Assign (f, fname);

{Блок проверки наличия файла на диске}

{$i-}

Reset(f);

{$i+}

code := IOResult;

If code <> 0 Then Begin

Writeln(‘Файла с таким именем не существует на диске!’);

Delay(2000); Exit;

End;

Assign(f1, ‘Temp’); Rewrite(f1) {Открыли новый файл для записи}

Writeln(‘Товары с датой поступления < 95 года будут удалены из базы’);

While Not Eof (f) do

Begin

Read (f, Sk); {Считали запись из старого файла.}

If Sk.Data.Year >= 95 Then Write(f1, Sk); {Записали в новый.}

End;

Close (f); Close (f1);

Erase(f); Rename (f1, fname);

{Старый файл удалили. Новый переименовали в старый.}

End;

 

При решении задачи ведения базы данных (ввод, добавление, удаление записей и т. п.) вводят дополнительное поле, в которое записывают признак существования записи. Если, например, запись удаляется, то ее не стирают с диска, а изменяют значение поля признака. При просмотре базы анализируют состояние поля признака. Удаленную таким образом запись можно восстановить.

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

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

 

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

В программе введены следующие обозначения переменных:

Ind – индексированный массив.

p – вспомогательный массив цен, необходимый для создания индексного массива.

Min, i, j, k, nn, c – вспомогательные переменные.

 

Программа.

Program Task5;

Uses CRT;

Const n = 100;

Type

Tf = String[20];

Date = Record day : 1..31; Mon : 1..12; Year : 1..99; End;

Sklad = Record

Name : Tf;

Data : Date;

Price : Real;

Count : Integer

End;

Var FileName : Tf; code : Byte; Ind : Array [1..n] Of Integer;

f : File Of Sklad; Sk : sklad; p : Array [1..n] Of Real;

Min : Real; i, j, k, nn, c : Integer;

{———————————————————————————————————}

Begin

Repeat

Write(‘Введите имя файла.’); Readln(FileName);

Clrscr;

Assign (f, FileName);

{Блок проверки наличия файла на диске}

{$i-}

Reset(f);

{$i+}

code := IOResult;

If code <> 0 Then Begin

Writeln(‘Файла с таким именем не существует на диске!’);

Delay(2000);

End;

Until code = 0;

{Формирование массива цен товаров базы данных}

i := 0;

While Not eof(f) do

Begin

i := i + 1;

Read(f, sk);

p[i] := Sk.price; {Массив цен}

End;

nn := Filesize(f);

For i := 1 To nn Do Ind[i] := i; {Инициировали индексированный массив}

{Блок формирования массива р в порядке возрастания}

For i := 1 To nn - 1 Do

Begin

Min := p[i]; k := i;

For j := i To nn do

If p[j] < Min Then Begin k := j; Min := p[j]; End;

p[k] := p[i]; p[i] := Min;

{Индексы массива р располагаются в индексированном массиве Ind}

{в порядке возрастания элементов массива р}

c := Ind[k]; Ind [k] := Ind[i]; Ind[i] := c;

End;

Reset(f);

For i := 1 To nn do

Begin

Seek(f, Ind[i] - 1); {Переход к компоненте с нужным номером}

Read(f, sk);

With Sk Do

Begin

Writeln(‘Наименование товара ‘, Name);

Writeln(‘Дата поступления ‘, Data.Day,’/’,Data.Mon,’/’,Data.Year);

Writeln(‘Цена за единицу товара ‘,Price : 9 : 2);

Writeln(‘Количество в шт. ‘,Count);

Delay(1000);

End;

End;

Writeln(‘Для продолжения нажмите <Enter>’);

Repeat Until keypressed;

Close (f);

End.

Текстовые файлы

Текстовые файлы– это файлы, в которых:

1) информация представляется в текстовом виде посредством символов в коде ASCII;

2) порции информации могут разделяться на строки. Признаком конца строки служит символ #13 (код 13 – CR). Он может быть объединен с символом перевода строки #10 (код 10 – LF);

3) конец файла обозначается явно символом ^Z (код 26);

4) при записи чисел, строк и логических значений они преобразуются в символьный (текстовый) вид;

5) при чтении чисел и строк они автоматически преобразуются из текстового представления в машинное.

Текстовый файл может выглядеть следующим образом:

 

[13][10]

Вы читаете текстовый файл, который [13][10]

может храниться на диске или печататься [13][10]

на принтере. [13][10]

В нем можно хранить цифровые записи чисел: [13][10]

123 456 789 0[13][10]

234 567 890 1[13][10]

1.2 3.4 5.60 4[13][10]

-100.254[13][10]

Конец файла[13][10]

[26]

 

Цифры в квадратных скобках – управляющие коды с тем же номером, т.е. [13] = #13. В файле они занимают по одному символу и в текстовых режимах на экран и принтер не выводятся (но управляют выводом).

Каждая строка заканчивается признаком конца строки, даже пустая. Для признака конца строки вводится специальное обозначение – Eoln (End of line). Самый последний символ в файле – признак его конца. Реально файл хранится как сплошная последовательность символов и разбивается на строки лишь при выводе на экран или печать. Пустой текстовый файл содержит один символ #26.

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

 

Var f : Text;

 

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

К компонентам текстового файла возможен только последовательный доступ – считать n-ю строку можно только после того, как уже считаны предыдущие n – 1 строк. Текстовый файл может быть открыт либо для чтения, либо для записи, либо для добавления данных в конец файла. Соответст­вующие процедуры:

Reset (f) – открывает текстовый файл для чтения;

Rewrite (f) – открывает текстовый файл для записи;

Append (f)– открывает текстовый файл для добавления данных в конец файла.

f – файловая переменная текстового файла.

Текстовые файлы можно записывать операторами Write и Writeln, а считывать с помощью операторов Read и Readln.

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

Процедура Read применительно к текстовому файлу считывает из файла данные в переменные строкового, символьного, целого и вещественного типов. При считывании в переменную символьного типа происходит считывание одного символа. При считывании в переменную строкового типа считывается максимально возможное для данной переменной число символов. Если это число превышает число символов в строке файла, то считывается вся строка до символов конца строки. Процедуру Read не рекомендуется использовать для ввода переменных типа String, так как она не способна “перепрыгнуть” через разделитель строк и читает только первую строку текстового файла. При считывании чисел сначала пропускаются ведущие пробелы и прочие незначащие символы (символы табуляции, символы конца строки), затем выделяется подстрока до первого незначащего символа или до маркера конца файла. Выделенная подстрока воспринимается далее как символьное представление целого или вещественного числа, которое затем преобразуется в число. Если цепочка символов не отвечает желаемому формату, выдается сообщение об ошибке.

Процедура Readln после считывания всех данных в указанные переменные пропускает все оставшиеся символы до символов конца строки, пропуская и сами эти символы. Следующее чтение из файла будет начинаться уже со следующей строки. Если при вызове процедуры Readln (f) не указывать в числе параметров переменные, в которые необходимо считать данные, то все символы до конца строки пропускаются. Так, при чтении в текстовом файле f строки:

 

12.3 13.4 14.5 15.6

 

оператором Readln(f, r1, r2) вещественные переменные r1 и r2 получат значения 12.3 и 13.4, после чего произойдет переход на другую строку, и следующие два числа (14.5 и 15.6) будут проигнорированы.

Список вывода в операторах Write и Writeln могут составлять константы, переменные целого, вещественного, символьного и строкового типов, а также произ­водные от них. Процедура Write выводит данные в текущую строку и не закрывает ее, т.е. следующие данные запишутся в ту же строку. Процедура Writeln приписывает символы #13 и #10 в конец строки. Так же, как и при выводе на экран, используется форматный вывод. Например, Writeln (f, a : 8 : 3, ‘S=‘, S : 4). Оператор Writeln(f) создает пустую строку, содержащую один только признак конца строки.