Изучай Haskell во имя добра! (Липовача) - страница 157

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

Исключения ввода-вывода происходят, когда что-то пошло не так при взаимодействии с внешним миром в действии ввода-вывода, являющемся частью функции >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)