putStrLn "Конец программы"

Посмотрим, как она теперь работает:

$ ./quotients 20 10

2

0

Конец программы

$ ./quotients

Неверное число параметров командной строки!

Конец программы

$ ./quotients 2 0

Деление на 0!

Конец программы

$ ./quotients a b

Неизвестное исключение: Prelude.read: no parse

Конец программы

В этом разделе мы разобрались с работой функций try, catch и catches, позволяющих обработать исключение, в том числе и возникшее в чистом коде. Заметьте ещё раз, что вся обработка выполнялась в рамках действий ввода-вывода. Посмотрим теперь, как работать с исключениями, которые возникают при выполнении операций ввода-вывода.

<p>Обработка исключений ввода-вывода</p>

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

import System.Environment

import System.IO

main = do

   (fileName:_) <– getArgs

   contents <– readFile fileName

   putStrLn $ "В этом файле " ++ show (length (lines contents)) ++

              " строк!"

Очень простая программа. Мы выполняем действие ввода-вывода getArgs и связываем первую строку в возвращённом списке с идентификатором fileName. Затем связываем имя contents с содержимым файла. Применяем функцию lines к contents, чтобы получить список строк, считаем их количество и передаём его функции show, чтобы получить строковое представление числа. Это работает – но что получится, если передать программе имя несуществующего файла?

$ ./linecount dont_exist.txt

linecount: dont_exist.txt: openFile: does not exist (No such file or directory)

Ага, получили ошибку от GHC с сообщением, что файла не существует! Наша программа «упала». Но лучше бы она печатала красивое сообщение, если файл не найден. Как этого добиться? Можно проверять существование файла, прежде чем попытаться его открыть, используя функцию doesFileExist из модуля System.Directory.

import System.Environment

import System.IO

import System.Directory

main = do

   (fileName:_) <– getArgs

   fileExists <– doesFileExist fileName

   if fileExists

      then do

         contents <– readFile fileName

         putStrLn $ "В этом файле " ++

                    show (length (lines contents)) ++

                    " строк!"

     else putStrLn "Файл не существует!"

Мы делаем вызов fileExists <– doesFileExist fileName, потому что функция doesFileExist имеет тип doesFileExist :: FilePath –> IO Bool; это означает, что она возвращает действие ввода-вывода, содержащее булевское значение, которое говорит нам, существует ли файл. Мы не можем напрямую использовать функцию doesFileExist в условном выражении.

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

import Prelude hiding (catch)

import Control.Exception

import System.Environment

countLines :: String -> IO ()

countLines fileName = do

  contents <- readFile fileName

  putStrLn $ "В этом файле " ++ show (length (lines contents)) ++

             " строк!"

handler :: IOException -> IO ()

handler e = putStrLn "У нас проблемы!"

main = do

  (fileName:_) <- getArgs

  countLines fileName `catch` handler

Здесь мы определяем обработчик handler для всех исключений ввода-вывода и пользуемся функцией catch для перехвата исключения, возникающего в функции countLines.

Попробуем:

Перейти на страницу:

Похожие книги