>Погладить посуду
>Вынуть салат из печи
Круто! Удалим ещё что-нибудь:
>$ ./deletetodo
>Ваши задания:
>0 – Погладить посуду
>1 – Вынуть салат из печи
>Что вы хотите удалить?
>0
Проверяя файл с заданиями, убеждаемся, что осталось только одно:
>$ cat todo.txt
>Вынуть салат из печи
Итак, всё работает. Осталась только одна вещь, которую мы в этой программе не учли. Если после открытия временного файла что-то произойдёт и программа неожиданно завершится, то временный файл не будет удалён. Давайте это исправим.
Чтобы гарантировать удаление временного файла, воспользуемся функцией >bracketOnError
из модуля >Control.Exception
. Она очень похожа на >bracket
, но если последняя получает ресурс и гарантирует, что освобождение ресурса будет выполнено всегда, то функция >bracketOnError
выполнит завершающие действия только в случае возникновения исключения. Вот исправленный код:
>import System.IO
>import System.Directory
>import Data.List
>import Control.Exception
>main = do
> contents <– readFile "todo.txt"
> let todoTasks = lines contents
> numberedTasks = zipWith (\n line –> show n ++ " – " ++ line)
> [0..] todoTasks
> putStrLn "Ваши задания:"
> mapM_ putStrLn numberedTasks
> putStrLn "Что вы хотите удалить?"
> numberString <– getLine
> let number = read numberString
> newTodoItems = unlines $ delete (todoTasks !! number) todoTasks
> bracketOnError (openTempFile "." "temp")
> (\(tempName, tempHandle) –> do
> hClose tempHandle
> removeFile tempName)
> (\(tempName, tempHandle) –> do
> hPutStr tempHandle newTodoItems
> hClose tempHandle
> removeFile "todo.txt"
> renameFile tempName "todo.txt")
Вместо обычного использования функции >openTempFile
мы заключаем её в >bracketOnError
. Затем пишем, что должно произойти при возникновении исключения: мы хотим закрыть и удалить временный файл. Если же всё нормально, пишем новый список заданий во временный файл; все эти строки остались без изменения. Мы выводим новые задания, удаляем исходный файл и переименовываем временный.