Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курс DELPHI_2003.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
530.71 Кб
Скачать

Строка и строки tStrings

В практике программирования чаще всего приходится работать со строками. Будь то обычный Edit или Memo, там используются строки. И даже если вы вставляете Edit, чтобы запросить у пользователя число, он все равно вводит его в качестве строки, и вам требуется преобразовать его в числовой тип, чтобы работать с ним, как с числом.

Итак, что такое строка? Фактически, это массив символов. Давайте посмотрим пример:

var

s : String;

a : array [1..7] of Char;

begin

s := 'Привет!';

ShowMessage(s); //Результат: вывод строки "Привет!"

a[1] := 'П';

a[2] := 'р';

a[3] := 'и';

a[4] := 'в';

a[5] := 'е';

a[6] := 'т';

a[7] := '!';

ShowMessage(a); //Результат: вывод строки "Привет!"

ShowMessage(s[1]); //Результат: вывод буквы "П"

ShowMessage(a[1]); //Результат: вывод буквы "П"

К сожалению, мы не можем просто так взять и присвоить массиву строку – это будет ошибкой, поэтому a := s не скомпилируется. Однако после того, как в массив мы посимвольно ввели текст, это получается уже полноценной строкой, поэтому компилятор разрешит директиву ShowMessage(a);, и программа отработает без ошибок. Разумеется, неразумно использовать массив символов там, где проще использовать переменную типа String. Этот пример показывает, что строка – это тот же массив символьного типа!

Однако мы с вами уже сталкивались и с типом TStrings – строки. Такой тип мы использовали в свойстве Lines компонента Memo и вы уже убедились, насколько мощный это тип и как легко с ним работать. Он позволяет добавлять или удалять строку из списка строк, с его помощью легко записать строки в файл или наоборот, загрузить их из файла. Тип TStrings присутствует во многих компонентах, и везде вы с ним можете производить такие вот действия! Раз уж мы заговорили о массивах, то проведем сравнение и для TStrings – это тот же массив, только не типа Char (символ), а типа String (строка):

a : array [1..10] of String;

Однако, хотя TStrings по своему образу похож на такой массив, он имеет массу дополнительных возможностей, поэтому используйте его везде, где встает речь о работе с набором строк!

ListBox – список выбора

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

Чтобы получить доступ к строкам списка, нужно воспользоваться свойством Items компонента ListBox. Это свойство имеет тип TStrings (строки), с таким типом мы сталкивались, когда обрабатывали компонент Memo.

Посмотрим на практике, как работать с данным компонентом. Создайте новое приложение, установите на него один компонент ListBox, и под ним – один Edit для вывода той строки, которую выбрал пользователь:

Рис. 12.1.  Внешний вид формы

Затем дважды щелкните по свойству Items компонента ListBox. Перед вами откроется уже знакомый редактор строк. Наберите названия операционных систем:

MS-DOS

Windows 3.10

Windows 95

Windows 98

Windows ME

Windows 2000

Windows XP

Windows Vista

Unix

Linux

OS/2

Каждое название операционной системы должно быть на отдельной строке. Нажмите "ОК", чтобы сохранить результат. Теперь создайте для компонента ListBox обработчик события onClick. Мы уже знаем, что это событие срабатывает всякий раз, когда пользователь выберет одну из строк в указанном списке.

В обработчике событий напишите только одну строку:

Edit1.Text := ListBox1.Items.Strings[ListBox1.ItemIndex];

Эта строка присваивает свойству Text компонента Edit тот текст, который хранится в выбранной строке списка ListBox. Свойство Items имеет свое свойство Strings, которое представляет собой массив из строк списка. Указав индекс массива, мы можем получить доступ к нужной строке. В примере в качестве индекса указано свойство ListBox1.ItemIndex, которое имеет тип Integer и возвращает элемент выбранной строки в массиве строк. Кстати, когда вы поставите точку и откроется список, там вы этого свойства не обнаружите, так что придется писать его вручную.

Сохраните пример в новую папку, откомпилируйте и посмотрите, как он работает.

Кроме того, мы имеем возможность программно добавлять в ListBox новую строку или удалять выбранную. Киньте на форму еще две кнопки и напишите на них – "Добавить строку", "Удалить строку". Добавлять будем строку, написанную в компоненте Edit, а удалять – выделенную.

Для кнопки "Добавить" впишите:

ListBox1.Items.Add(Edit1.Text);

а для кнопки "Удалить":

ListBox1.Items.Delete(ListBox1.ItemIndex);

Сохраните проект, откомпилируйте и посмотрите, как он работает.

Часто бывает необходимым предоставить пользователю возможность множественного выбора. Пользователь, удерживая клавишу <Ctrl>, щелкает по строкам и выбирает нужные. Для этого у ListBox имеется свойство MultiSelect (множественный выбор). По умолчанию оно равно False, то есть запрещает пользователю выбрать несколько строк. Создадим еще одно приложение, в котором продемонстрируем возможность множественного выбора.

Установите на форму один ListBox и один Memo, ниже – кнопку с надписью "Копировать". При нажатии на кнопку будем искать у ListBox выделенные строки и копировать их в Memo. На рисунке 12.2 в левой части установлен компонент ListBox, а в правой – Memo.

Рис. 12.2.  Внешний вид приложения

Войдите в редактор строк ListBox и напишите там

Строка № 1

Строка № 2

и так далее, пока не заполните ListBox так, чтобы его не нужно было прокручивать. Установите свойство MultiSelect у ListBox в True. Не забудьте очистить Memo. Теперь для кнопки напишем следующий код:

procedure TForm1.Button1Click(Sender:TObject);

var

i : Integer;

begin

Memo1.Clear;

for i := 0 to ListBox1.Items.Count - 1 do

if ListBox1.Selected[i] then

Memo1.Lines.Add(ListBox1.Items.Strings[i]);

end;

Вначале мы очистили Memo, чтобы пользователь мог несколько раз опробовать выбор и копирование строк, и строки в Memo не скапливались.

Затем мы создаем цикл, чтобы обрабатывать ListBox построчно. Первая строка всегда имеет нулевой индекс, а свойство Count во всех объектах TStrings возвращает количество строк в объекте. Нам приходится вычесть единицу, так как индексы начинаются не с 1, а с 0. Далее в цикл мы поместили условный оператор if – если условие верно (то есть строка выделена), то производим добавление строки в Memo. За это отвечает свойство Selected (Выделено) с указанием индекса строки. Если оно равно True, то строка выделена.

ComboBox – выпадающий список

Такие списки вы часто встречаете при работе с Windows и различными программами. Действуют и работают они также как ListBox, с той только разницей, что выглядят по-другому, и не позволяют выбрать несколько элементов сразу. Создайте новое приложение, добавьте туда только один ComboBox и один Edit:

Рис. 12.3.  Внешний вид приложения

Вызовите редактор строк ComboBox (свойство Items) Впишите несколько городов:

Москва

Санкт-Петербург

Киев

Минск

Ташкент

Душанбе

Свойство ItemIndex, которое, как мы знаем, указывает индекс выделенной строки, по умолчанию установлено в -1. Это означает, что ни одна строка не выбрана. Если установить его в 0, то в поле ввода текста появится первая строка списка. Оставим -1, чтобы строк не было видно.

Создадим обработчик события OnChange для компонента ComboBox, и там напишем только одну строку:

Edit1.Text := ComboBox1.Items.Strings[ComboBox1.ItemIndex];

Как видите, это почти точная копия работы с ListBox. Сохраните пример в новую папку, откомпилируйте и попробуйте как он работает.

Управление циклами

Вы знаете уже практически все циклы – for..do, while..do, repeat..until. Однако этими циклами можно еще и управлять. Для этого служат директивы break и continue.

Break – прерывание цикла. Если внутри цикла встретится такой оператор, происходит немедленный выход из цикла. Как правило, этот оператор используют совместно с управляющей структурой if, например:

if a <> b then break;

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

Continue – прерывание текущего шага цикла. В отличие от break, continue не прекращает цикл вовсе, а лишь прерывает дальнейшую обработку этого шага цикла, после чего цикл сразу начинается со следующего шага. Способ применения такой же, как у break. Рассмотрим работу continue на практическом примере.

Нам нужно разделить число 10 на число от -3 до 3 включительно, и результат вывести в ListBox. Поскольку выводить будем также целые числа, нам поможет функция Round(), которая принимает вещественное число, округляет его до ближайшего целого и это целое возвращает. Также мы знаем, что на ноль делить нельзя, это ошибка. Для того, чтобы не допустить это деление, мы прервем этот шаг цикла с помощью директивы continue.

Создайте новое приложение. Установите на форму ListBox, а под ним – кнопку:

Рис. 12.4.  Внешний вид приложения

При нажатии на кнопку, напишите следующий код:

procedure TForm1.Button1Click(Sender: TObject);

var

i, r : Integer;

begin

for i := -3 to 3 do begin

if i = 0 then begin

ListBox1.Items.Add('Нанольделитьнельзя!');

Continue;

end; //if

r := Round(10/i);

ListBox1.Items.Add('10/'+IntToStr(i)+'='+IntToStr(r));

end; //for

end;

В тот момент, когда счетчик i станет равным 0, выполнится тело условия if, и после оператора Continue цикл сразу перейдет на новый виток, пропустив деление.

Занятие 13

Самостоятельное задание

В примере мы жестко задали условия: делить именно число 10 на диапазон чисел от -3 до 3. Измените пример так, чтобы пользователь мог сам указать какое число нужно делить, и на какой диапазон. Это несложное задание, однако, придется подключить к работе мозги.

Диалоги

Что такое диалоги в Delphi? Это невизуальные, то есть, невидимые пользователю компоненты, которые выполняют стандартные для Windowsдиалоги пользователя и программы. Например, диалог открытия или сохранения файла, диалог выбора шрифта или цвета, и т.п. Любая серьезная программа содержит такие диалоги. Работа с ними несложная, каждый диалог содержит буквально по два свойства, которые нам будут нужны. Изучим работу диалогов на примере.

Открываем новый проект. Сразу же форму переименовываем в fMain, в свойстве Caption пишем "Мой блокнот". Сохраните проект под именем Editor. Это будет более профессиональная версия редактора текстов, какой мы уже делали. Считайте, что это вторая версия программы, что и напишете в окне fAbout.

Установите на форму компонент Memo, удалите весь текст из его свойства Lines. Поверх Memo установите MainMenu и PopupMenu. Перейдите на вкладку Dialogs в палитре компонентов, и также поверх Memo установите OpenDialog, SaveDialog, FontDialog и ColorDialog. Все эти компоненты не визуальные, пользователь их видеть не сможет. У Memo сразу же укажите свойство Align=alClient, а свойство ScrollBar=ssVertical. Рекомендую для лучшего вида выбрать шрифт (свойство Font) Times New Roman, а размер = 12. На всех компьютерах с русской Windows есть этот шрифт, однако если вы будете писать программу для продажи в Интернет, лучше оставить шрифт по умолчанию, это гарантирует, что он будет читаться на всех компьютерах в мире.

Во всех диалогах есть один метод, который нас интересует – Execute. Это метод логического типа, он возвращает True, если диалог с пользователем произошел успешно (например, пользователь выбрал открываемый файл), и False в противном случае (например, пользователь отказался от выбора файла). В связи с этим, диалоги обычно применяют вместе с проверкой:

if OpenDialog1.Execute then...

В диалогах, связанных с файлами (OpenDialog – открыть файл, и SaveDialog – сохранить файл), есть свойство FileName, которое возвращает строку – адрес и имя выбранного файла. Вот, собственно, и все, что нам нужно от этих диалогов!

ДиалогFontDialog в свойстве Font возвращает выбранный шрифт. Это свойство непростое, оно имеет тип TFont, и присвоить его можно только другому шрифту, например:

Memo1.Font := FontDialog1.Font;

Точно также, диалогColorDialog возвращает свойство Color – цвет, имеющее тип TColor, и это свойство можно присвоить только объекту, имеющему такой же тип:

Memo1.Color := ColorDialog1.Color;

Продолжимнашпроект. Откройте редактор главного меню. Создайте раздел "Файл" и подразделы "Открыть", "Сохранить", "Закрыть", "-" и "Выход". Создайте раздел "Параметры" и подразделы "Выбрать шрифт" и "Выбрать цвет". Создайте раздел "Справка" и подраздел "О программе".

Теперь открываем редактор PopupMenu и вписываем разделы "Открыть", "Сохранить", "Закрыть", "-" и "Выход". Сразу же на форме выделяем саму форму (это можно сделать в окне Object – TreeView), и в свойстве PopupMenu выбираем наше меню. Теперь это меню откроется, если пользователь щелкнет правой кнопкой по любому месту на форме.

Теперь нам нужно настроить фильтры в диалогахOpenDialog (Открыть) и SaveDialog (Сохранить). Фильтры позволяют отображать в этих диалогах только нужные форматы файлов, и для этого выбора используется маска файла. К примеру, маска *.* будет отображать файлы всех типов! Дважды щелкнув по свойству FilterдиалогаOpenDialog, откройте редактор фильтров. В первой колонке напишите "Текстовые документы", во второй – "*.txt". В строке ниже укажите "Все файлы", а во второй колонке – "*.*". Тоже самое сделайте для диалогаSaveDialog:

Рис. 13.1.  Настройка фильтра файловых диалогов

Теперь, открывая файл, вы увидите только эти два типа файлов. Подумаем о том, что программа должна знать – изменился ли текст в Memo. Ведь пользователь может закрыть программу и не сохранить текст, а потом будет ругать программиста за то, что он этого не предусмотрел. При этом имеем в виду, что у нас есть много команд меню, значит, будет много процедур. А чтобы дать программе знать, изменился ли текст, разумнее всего создать переменную логического типа – изменился текст, присваиваем ей True, иначе False. Чтобы с этой переменной можно было работать из всех процедур, она должна быть глобальной. Делаем глобальную переменную перед словом implementation:

izmen : Boolean; //изменился ли текст в Memo

Событию onChange компонента Memo присвойте строку:

izmen := True;

Как только изменится текст в Memo, переменная тут же будет выдавать истину.

Теперь еще один момент – программа должна знать имя и адрес открытого файла. Если имени файла нет, то программа будет выводить диалоговое окно, а если мы файл открывали и имя есть, то программа просто будет перезаписывать тот файл без вывода диалогового окна. Стало быть, делаем еще одну глобальную переменную:

myfile : String; //Адрес и имя открытого файла

Я специально не беру широко распространенные слова "File" или "FileName", так как они могут быть зарезервированными или в компонентах могут быть свойства с такими именами, в результате получится конфликт названий.

Соображаем дальше. Открыть файл можно будет командой меню "Файл – Открыть", либо командой PopupMenu "Открыть". Стало быть, нам придется дважды писать один и тот же код? А если он будет большим и сложным? Можно конечно и скопировать его, компилятор это выдержит, и программа будет работать нормально. А как же оптимизация кода? Два одинаковых кода в программе будут занимать в два раза больше места в памяти и на диске! Для этого мы имеем пользовательские функции и процедуры.

Вспоминаем – функцию или процедуру мы должны описать ВЫШЕ того места, где будем ее использовать, значит, первую нашу процедуру мы должны описать в разделе implementation, прямо под строчкой {$R *.dfm}.

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

Memo1.

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

fMain.Memo1.

Однако такой код будет не слишком удобен – каждый раз придется обращаться к компонентам через форму. Лишний набор кода, излишне длинный текст. Выход есть – функция with (с). Эта функция имеет вид:

with fMain do begin

...

end;

где fMain – имя формы с нужными нам компонентами. Теперь между скобок begin...end этой конструкции мы можем обращаться к компонентам формы напрямую. Пишем общую для всех процедуру открытия файла:

{Процедура открытия файла}

procedure Otkrivaem;

begin

with fMain do begin //делатьвместесформой

if OpenDialog1.Execute then begin //еслидиалогвыполнен

//присваиваем переменной myfile адрес и имя выбранного файла:

myfile := OpenDialog1.FileName;

//читаемэтотфайлв Memo:

Memo1.Lines.LoadFromFile(myfile);

izmen := False; //файлтолькооткрыт, измененийещенет

end; //if

end; //with

end;

Теперь создаем обработчик событий для команды меню "Файл – Открыть". Там вызываем нашу процедуру:

Otkrivaem;

Тоже самое делаем для команды PopupMenu "Открыть".

Дальше – сложней. Открыть файл просто, но вот при сохранении нам придется учитывать много вещей:

  1. Пользователь может сохранять новый файл, который он только что набрал. То есть, переменная myfile пуста и не содержит имя файла. В таком случае придется выводить диалогSaveDialog1, чтобы пользователь указал имя файла. Когда он укажет имя, присвоить его переменной myfile и сохранить Memo в указанный файл.

  2. Пользователь может сохранять новый файл, как в предыдущем примере. Мы выведем диалог, но он его не завершит – нажмет кнопку "отмена" или красный крестик в верхней части окна справа. Значит, сохранять ничего не нужно, мы ведь не знаем, куда сохранять! Но придется его предупредить, что файл не сохранен!

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

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

Видите? Пользоваться диалогами несложно, но как много приходится учитывать во время проверок действий пользователя! Нужно учитывать каждую мелочь, иначе вам скажут, что ваша программа полна "багов" - мелких неисправностей. Итак, пишем процедуру сохранения, сразу под процедурой открытия:

{Процедура сохранения файла}

procedure Sohranyaem;

begin

with fMain do begin

//если изменений не было, выходим из процедуры,

//ничегонеделая:

if not izmen then Exit;

//Если файл уже открывался, и в переменной myfile

//есть его адрес и имя, просто перезаписываем этот файл:

if myfile <> '' then begin

Memo1.Lines.SaveToFile(myfile);

izmen := False;

Exit; //выходим после сохранения

end; //if

{Файл новый, переменная myfile еще пуста. Дальше есть два варианта:

пользователь выберет или укажет файл в диалоге, или не сделает этого}

//есливыбралфайл:

if SaveDialog1.Execute then begin

//прописываем адрес и имя файла в переменную:

myfile := SaveDialog1.FileName;

//если нет расширения *.txt то добавляем его:

if copy(myfile, length(myfile)-4, 4) <> '.txt' then

myfile := myfile + '.txt';

//сохраняем Memo вуказанныйфайл:

Memo1.Lines.SaveToFile(myfile);

//файл сохранен, изменений нет:

izmen := False;

end //if

//если не выбрал файл:

else ShowMessage('Вы не указали имени файла, файл не сохранен!');

end; //with

end;

Приведенный выше код имеет достаточно подробные комментарии, так что все должно быть понятно. Новое, что вы могли увидеть – директива Exit. Эта директива досрочно завершает работу процедуры (или функции). То есть, если выполнено условие и отработан нужный код, Exit заставляет процедуру завершить работу. Остальной код, который есть в этой процедуре, не выполняется.

Теперь мы можем создать обработчик главного меню "Файл – Сохранить", и там прописать вызов этой процедуры:

Sohranyaem;

Тоже самое делаем для команды PopupMenu "Сохранить".

Далее идет команда главного и всплывающего меню "Выход". Тут все просто, в обоих случаях пишем команду

Close;

Далее идет раздел "Параметры". Создаем обработчик "Выбрать шрифт", там все просто:

if FontDialog1.Execute then

Memo1.Font := FontDialog1.Font;

Точно также и с подразделом "Выбрать цвет":

if ColorDialog1.Execute then

Memo1.Color := ColorDialog1.Color;

Далее вернемся в раздел "Файл". Здесь у нас остался подраздел "Закрыть". Создаем для него обработчик, и запишем текст:

{если файл не сохранен, предупреждаем пользователя об этом. Если он

желает сохранить, то вызываем процедуру сохранения:}

if izmen then

if Application.MessageBox('Файлизменен. Сохранить?', 'Внимание!',

MB_YESNO+MB_ICONQUESTION) = IDYES then Sohranyaem;

//теперь закрываем текущий файл:

Memo1.Clear; //очищаем Мемо

myfile := ''; //нет имени текущего файла

izmen := False; //нет изменений

Здесь не так много текста, и он вполне понятен, поэтому не будем для него делать отдельную процедуру. Просто скопируйте текст, создайте обработчик событий для "Закрыть" PopupMenu и вставьте этот же текст туда.

Примерно тоже самое нам придется сделать и для события формы onClose – ведь мы заранее не можем знать, каким образом пользователь закроет форму, через команду главного меню "Файл – Закрыть", через команду всплывающего меню "Закрыть", нажмет ли он красный крестик наверху или нажмет горячие клавиши <Alt + F4>! Поэтому для проверки – есть ли в программе несохраненный текст, мы используем событие onClose, которое случается при попытке закрыть форму, то есть в данном случае всю программу. Здесь код будет таким же, как и в предыдущем примере, но раз мы закрываем всю программу, нам не нужно выполнять блок закрытия файла. Просто проверим, есть ли изменения, и если они есть, выведем запрос – сохранить ли их. Если пользователь ответит "Да", значит, вызовем процедуру сохранения:

{если файл не сохранен, предупреждаем пользователя об этом. Если он

желает сохранить, то вызываем процедуру сохранения:}

if izmen then

if Application.MessageBox('Файлизменен. Сохранить?', 'Внимание!',

MB_YESNO+MB_ICONQUESTION) = IDYES then Sohranyaem;

Теперь введем еще одну команду для обоих меню – "Очистить". Она будет предназначена на случай, если пользователь введет абракадабру, а потом решит очистить текст. Я намеренно оставил это действие на конец, чтобы вы могли усвоить, что в процессе работы над программой меню может изменяться!

Откройте редактор MainMenu, в разделе "Файл" в самом низу добавьте подраздел "Очистить". Затем мышкой перетащите его на место линии разделов, при этом линия опустится вниз, а новый подраздел встанет на ее место:

Рис. 13.2.  Вид главного меню

Создайте для команды "Очистить" обработчик, и впишите туда текст:

{MainMenu - Файл - Очистить}

procedure TfMain.N17Click(Sender: TObject);

begin

//очищаемМемо:

Memo1.Clear;

//если открытого файла нет, то нет и изменений:

if myfile = '' then izmen := false

//иначе текст изменен:

else izmen := true;

end;

Сделайте то же самое для всплывающего меню – создайте раздел "Очистить", а в его обработчик скопируйте тот же текст.

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

Все, сохраняйте, компилируйте и проверяйте, как работает программа! У нас получился несложный, но вполне профессиональный редактор текстов. Одно замечание: текст мы сохраняем, но вот выбранный шрифт и цвет окна Вы сохранить не можете, об этом мы будем говорить на других лекциях, когда научимся сохранять параметры.

Обработка исключений в Delphi

В процессе разработки и выполнения программ возникают следующие виды ошибок:

Синтаксические

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

 

Логические

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

 

Динамические

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

Исключения генерируются компонентами Delphi для различных событий, таких как присвоение свойству значения, выходящего за допустимые пределы, или попытка индексировать несуществующий элемент массива.

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

Вот некоторые типы операций, которые могут привести к исключительным ситуациям:

– обработка файла;

– выделение памяти;

– работа с ресурсами системы;

– работа с объектами и формами, создаваемыми во время выполнения программы;

– аппаратные конфликты и конфликты операционных систем.

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

В Delphi для обработки динамических ошибок в выполняемый файл приложения встраиваются специальные фрагменты кода, предназначенные дл реагирования исключений. Механизмы обработки ошибок в Delphi инкапсулированы в класс Exception – базовый класс. Всего имеется сотни классов исключения:

EAbort Аварийное завершение работы без диалогового окна

EAbstractError Абстрактная ошибка метода

AssertionFailed Утверждают неудавшийся запрос

EBitsError Булев массив ошибок

ECommonCalendarError Календарная ошибка

EDateTimeError Ошибка DateTime

EMonthCalError Ошибка месяца

EConversionError Вызывается Convert

EConvertError Ошибка конвертирования объекта

EDatabaseError Ошибка базы данных

EExternal Ошибка аппаратных средств/Windows

EAccessViolation Нарушение прав доступа

EControlC Произошло аварийной завершение работы пользователем

EExternalException Другая Внутренняя ошибка

EIntError Целочисленная ошибка

EDivByZero Деление на ноль

EIntOverflow Переполнение целого числа

ERangeError Вне диапазона значений

EMathError Ошибка с плавающей запятой

EInvalidArgument Плохое значение аргумента

EInvalidOp Несоответствующая операция

EOverflow Значение слишком большое

EUnderflow Значение слишком маленькое

EZeroDivide Деление на ноль

EStackOverflow Серьёзная проблема Delphi

EHeapException Проблемы динамической памяти

EInvalidPointer Плохой указатель памяти

EOutOfMemory Нет возможности распределить память

EInOutError Ошибка ввода/вывода

EInvalidCast Ошибка произведенная объектом

EInvalidOperation Плохая операция компонента

EMenuError Ошибка пункта меню

EOSError Ошибка операционной системы

EParserError Ошибка синтаксического анализа

EPrinter Ошибка принтера

EPropertyError Ошибка свойства класса

EPropReadOnly Недопустимое обращение к свойству

EPropWriteOnly Недопустимое обращение к свойству

EThread Ошибка потока

EVariantError Различная ошибка

Для обработки исключений в приложении есть один глобальный обработчик и несколько специализированных процедур-обработчиков, реагирующих на соответствующие исключения. Каждое исключение обрабатывает свой специализированный локальный обработчик. Исключение, которое не имеет своего локального обработчика, обрабатывается глобальным обработчиком приложений.

Глобальная обработка

Механизм глобальной обработки исключений реализуется через объект Application, который есть в любом приложении. При получении от операционной системы сообщения об исключении объект Application генерирует событие OnException, обработчик которого и является глобальным обработчиком исключений. По умолчанию на это событие для всех видов динамических ошибок, не имеющих своего обработчика, реагирует метод HandleException приложения. В теле этого метода вызывается метод ShowException приложения, выводящий на экран диалоговое окно с описанием возникшего исключения. Такая обработка не устраняет причину исключения, но обеспечивает пользователя информацией об ошибке и облегчает ее поиск и устранение.

 

Локальная обработка

Для работы с локальными обработчиками исключений в состав языка введены две конструкции: try … finally и try … except. Обе конструкции имеют похожий синтаксис, но разное назначение. Блоки try включают в себя инструкции программы, при выполнении которых может возникнуть исключение. Выбор конструкции зависит от применяемых инструкций программы и действий, выполняемых при возникновении ошибки. Конструкции try могут содержать одну или более инструкций, а также быть вложенными друг в друга.

 

Конструкция try … finally состоит из двух блоков и следующую форму:

try

// инструкции, выполнение которых может вызвать ошибку

finally

// инструкции, которые должны быть выполнены даже в случае ошибки

end;

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

Конструкция работает следующим образом: если в любой из инструкций блока try возникает исключение, то управление передается первой инструкции блока finally. Если же исключение не возникло, то последовательно выполняются все инструкции обоих блоков.

 

На рисунке представлена логика работы оператора try…finally…end.

 

Конструкции try … except также состоит из двух блоков и имеет следующую форму:

try

// инструкции, выполнение которых может вызвать ошибку

except

// инструкции, которые должны быть выполнены в случае ошибки

end;

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

Конструкция работает следующим образом: если в инструкциях блока try возникает исключение, то управление передается первой инструкции блока except. Если же исключение не возникло, то инструкции блока except не выполняются.

 

На рисунке представлена логика работы оператора try…except…end.

 

 

Схема создания смешанных блоков защищенных операторов и защищенных ресурсов:

{Выделение ресурса}

try

try

{операторы, которые могут вызвать ошибку(сгенерировать исключение)}

except

{Операторы обработки исключений}

end;

finally

{Освобождение реурса}

end;

При необходимости исключение можно сгенерировать программно. Для этого используется инструкция raise, которая создает объект-исключение – экземпляр класса Exception. Инструкция raise имеет следующий синтаксис:

Raise ClassException.Method;

ClassException является классом исключения, на основе которого создается объект-исключение, а конструктор Method выполняет создание объекта-исключения, а для создания объектов-исключений чаще всего используются методы Create и CreateFmt классов исключений.

ПРИМЕР:

if Length(Edit1.Text)>5 then

Raise Exception.Create('слишком длинная строка');