Файлы и потоки
ОБРАБОТКА ИСКЛЮЧЕНИЙ В БЛОКАХ TRY ... CATCH
Синтаксис блоков try ... catch
Наиболее кардинальный путь борьбы с исключениями — отлавливание и обработка их с помощью блоков try ... catch.Синтаксис этих блоков следующий:
try
<
Исполняемый код
}
catch ( TypeToCatch )
{
Код, исполняемый в случае ошибки
}
Операторы блока catchпредставляют собой обработчик исключения. Параметр TypeToCatchможет быть или одним из целых типов (int, charи т.п.), или ссылкой на класс исключения, или многоточием, что означает обработку любых исключений. Смысл параметров целого типа будет рассмотрен ниже в разд. 1.12.6.1. А пока остановимся на случае, когда параметр является ссылкой на класс исключений.
Операторы обработчика выполняются только в случае генерации в операторах блока try исключения типа, указанного в заголовке catch. После блока tryможет следовать есколько блоков catchдля разных типов исключений. Таким образом, в обработчиках catchвы можете предпринять какие-то действия: известить пользователя о возникшей проблеме и подсказать ему пути ее решения, принять какие-то меры к исправлению ошибки (например, при переполнении заслать в результат очень большое число соответствующего знака) и т.д. Наиболее ценным является то, что вы можете определить тип сгенерированного исключения и дифференцированно реагировать на различные исключительные ситуации. Причем перехват исключения блоком catchприводит к тому, что это исключение далее не обрабатывается стандартным образом, т.е. пользователю не предъявляется окно с непонятными ему английскими текстами.
Приведем пример обработки исключений. Пусть в вашем приложении имеется два окна редактирования Editl и Edit2,в которых пользователь вводит действительные числа типа float.Приложение должно разделить их одно на другое. При этом возможен ряд ошибок: пользователь может ввести в окно символы, не преобразуемые в целое число, может ввести слишком большое число, может ввести вместо делителя нуль, результат деления может быть слишком большим для типа float.
Следующий код отлавливает все эти ошибки:
float А;
try
{
А = StrToFloat(Editl->Text) / StrToFloat(Edit2->Text);
}
catch(EConvertError&)
{
Application->MessageBox("Вы ввели ошибочное число",
"Повторите ввод",МВ_ОК);
}
catch(EZeroDivide&)
{
Application->MessageBox("Вы ввели нуль",
"Повторите ввод",МВ__ОК) ;
}
catch(EOverflows)
{
Application->MessageBox("Переполнение",
"Ошибка вычислений",МВ_ОК);
if (StrToFloat(Editl->Text) * StrToFloat(Edit2->Text) >= 0)
A = 3.4E38;
else A = -3.4E38;
}
Если пользователь ввел неверное число (например, по ошибке нажал не цифру, а какой-то буквенный символ), то при выполнении функции StrToFloatвозникнет исключение класса EConvertError.Соответствующий обработчик исключения сообщит пользователю о сделанной ошибке и посоветует повторить ввод.
Аналогичная реакция последует на ввод пользователем в качестве делителя нуля (класс исключения EZeroDivide).Если возникает переполнение, то соответствующий блок catchперехватывает исключение, сообщает о нем пользователю и исправляет ошибку: заносит в результат максимально возможное значение соответствующего знака.
Поскольку исключения образуют иерархию, рассмотренную в разд. 1.12.3, можно обрабатывать сразу некоторую совокупность исключений, производных от одного базового исключения. Для этого надо в заголовке блока catchуказать имя этого базового исключения. Например, исключения EZeroDivide(целочисленное деление на нуль), EOverflow(переполнение при целочисленных операциях), ElnvalidArgument(выход числа за допустимый диапазон) и некоторые другие являются производными от класса исключений EMathError.Поэтому все их можно отлавливать с помощью одного блока catch,например, такого:
catch(EMathErrors)
{
Application->MessageBox ("Ошибка вычислений","Повторите ввод", MB_OK);
}
Правда, в этом случае не конкретизируется причина прерывания исключений. Однако такая конкретизация возможна, если воспользоваться свойствами исключений. Все исключения имеют свойство Message,которое представляет собой строку, отображаемую пользователю при стандартной обработке исключений.
Чтобы воспользоваться свойствами исключений, надо в заголовке блока catchне только указать тип исключения, но и создать временный указатель на объект этого типа. Тогда через имя этого объекта вы получаете доступ к его свойствам.
Ниже приведен пример использования свойств исключений при перехвате исключений, наследующих классу EMathError:
catch(EMathErrors E)
{
AnsiString S = "Ошибка вычислений : ";
if(E.Message == "EZeroDivide") S += "деление на нуль";
if(E.Message == "EOverflow") S += "переполнение";
if(E.Message == "EInvalidArgument") S += "недопустимое число";
Application->MessageBox(S.c_str(), "Повторите ввод", МВ_ОК);
}
Вводимое в этом операторе имя ссылки на исключение Е носит сугубо локальный характер и вводится только для того, чтобы можно было сослаться на свойство Messageпо имени объекта исключения.
Как уже говорилось выше, если в заголовке блока catchуказано многоточие то этот блок перехватит любые исключения:
catch(...)
(
ShowMessage("Призошла ошибка.");
}
Блок catch(...) может сочетаться и с другими блоками catch,но в этом случае он должен, конечно, располагаться последним. Поскольку этот блок перехватит все исключения, то все блоки, следующие за ним, окажутся недоступными. C++Builder следит за этим. Если блок catch(...)оказался не последним, вам будет выдано компилятором сообщение об ошибке с текстом: "The handler must be last" ("Обработчик должен быть последним").
Следует отметить некоторую опасность применения блока catch(...).Перехват всех исключений способен замаскировать какие-то непредвиденные ошибки в программе, что затруднит их поиск и снизит надежность работы.
Работа с файлами в C++Builder может производиться несколькими принципи-
ально различными (с точки зрения пользователя) способами:
• использование библиотечных компонентов
• работа с файлами как с потоками в стиле С
• работа с файлами как с потоками в стиле С++
Рассмотрим эти возможности.
2.10.1 Файловый ввод/вывод с помощью компонентов
Работа с текстовыми файлами может осуществляться с помощью методов
LoadFromFile и SaveToFile,имеющихся у классов TStrings и TStringList.Эти
Типы данных в языке C++ 155
классы описывают списки строк и обладают множеством методов, позволяющих
манипулировать строками.
Если вы хотите в своем приложении прочитать содержимое некоторого тексто-
вого файла, обработать текст и сохранить его в файле, вы можете сделать это сле-
дующим образом. Объявите и создайте две глобальные переменные: список типа
TStringList,в котором будет храниться текст файла, и строковую переменную
типа AnsiString,в которой можете сформировать имя файла. Например:
TStringList *List = new TStringList;
AnsiString SFile = "Test.txt";
He забудьте только, что если требуемый файл расположен не в текущем ката-
логе и вам надо указать путь к файлу, то обратные слэши в записи пути должны
быть сдвоенные (см. разд. 1.5.1). Например, если вам требуется файл "c:\My-
Test\Test.txt", то вы должны записать его как "с:\\MyTest\\Test.txt".
В момент, когда вы хотите загрузить в свой список файл, надо выполнить опе-
ратор
List->LoadFromFile(SFile);
Впрочем, ограничиться таким оператором можно, если есть уверенность, что
требуемый файл существует. В противном случае код надо несколько усложнить,
чтобы можно было перехватить сгенерированное исключение. Например:
try{
List->LoadFromFile(SFile);
}
catch(.. . ) {
ShowMessage("Файл \"" + SFile +"\" не найден");
}
Если файл нормально загрузился в список List,вы можете работать с его тек-
стом. Текст расположен в свойстве списка Strings[int Index],в котором каждая
строка имеет тип AnsiString. Индексы начинаются с нуля. Для нашего примера
List->Strings[0]— это первая строка, List->Strings[l]— вторая и т.д.
Для списков типа TStringListпредусмотрено множество методов. При обра-
ботке отдельных строк вы можете использовать операции и методы, предусмотрен-
ные для строк типа AnsiString(см. разд. 2.5.2 и соответствующие разделы гл. 3).
Если вы хотите сохранить файл после проведенного редактирования, можно
выполнить оператор
List->SaveToFile(SFile);
где SFile содержит прежнее или новое имя файла.
При открытии и сохранении файла вы можетевоспользоваться стандартными
диалогами Windows, вызываемыми через соответствующие компоненты С++Ви-
ilder.
Если вы открываете файл для того, чтобы пользователь мог его просмотреть,
что-то в нем отредактировать и сохранить, вы можете обойтись без описанного
выше объекта типа TStringList.Для этих целей проще воспользоваться много-
строчными окнами редактирования типов ТМетоили TRichEdit.В последнем
случае вы можете работать не только с обычными текстовыми файлами, но и
с файлами в обогащенном формате RTF. Свойства Linesэтих компонентов имеют
тип TStrings,что позволяет применять к ним непосредственно методы LoadFrom-
File и SaveToFile.Например:
Memol->Lines->LoadFromFile(SFile);
RichEditl->Lines->LoadFromFile(SFile);
Через компоненты C++Builder можно работать не только с текстовыми файла-
ми, но и с файлами изображений и мультимедиа.