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

– действие ввода-вывода, которое вернёт содержимое файла в результате. Функция похожа на функцию >getContents. Единственное отличие – функция >getContents читает со стандартного входа (то есть с терминала), в то время как функция >hGetContents принимает дескриптор файла, из которого будет происходить чтение. Во всех остальных смыслах они работают одинаково. Так же как и >getContents, наша функция >hGetContents не пытается прочитать весь файл целиком и сохранить его в памяти, но читает его по мере необходимости. Это очень удобно, поскольку мы можем считать, что идентификатор >contents хранит всё содержимое файла, но на самом деле содержимого файла в памяти нет. Так что даже чтение из очень больших файлов не отожрёт всю память, но будет считывать только то, что нужно, и тогда, когда нужно.



Обратите внимание на разницу между дескриптором, который используется для идентификации файла, и его содержимым. В нашей программе они привязываются к именам >handle и >contents. Дескриптор – это нечто, с помощью чего мы знаем, что есть наш файл. Если представить всю файловую систему в виде очень большой книги, а каждый файл в виде главы, то дескриптор будет чем-то вроде закладки, которая показывает нам, где мы в данный момент читаем (или пишем), в то время как идентификатор >contents будет содержать саму главу.

С помощью вызова >putStr contents мы распечатываем содержимое на стандартном выводе, а затем выполняем функцию >hClose, которая принимает дескриптор и возвращает действие ввода-вывода, закрывающее файл. После открытия файла с помощью функции >openFile вы должны закрывать файлы самостоятельно!

Использование функции withFile

То, что мы только что сделали, можно сделать и по-другому – с использованием функции >withFile. Сигнатура этой функции:

>withFile :: FilePath –> IOMode –> (Handle –> IO a) –> IO a

Она принимает путь к файлу, режим открытия файла и некоторую функцию, принимающую дескриптор и возвращающую некое действие ввода-вывода. Функция >withFile вернёт действие ввода-вывода, которое откроет файл, сделает с ним то, что нам нужно, и закроет его. Результат, помещённый в заключительном действии ввода-вывода, будет взят из результата переданной нами функции. С виду это может показаться сложным, но на самом деле всё просто, особенно если использовать анонимные функции. Вот как можно переписать предыдущий пример с использованием функции >withFile:

>import System.IO


>main = do

>    withFile "girlfriend.txt" ReadMode (\handle –> do

>        contents <– hGetContents handle

>        putStr contents)

Функция