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

Аналог конструктора >: для строк байтов называется >cons. Он принимает байт и строку байтов и помещает байт в начало строки.

>ghci> B.cons 85 $ B.pack [80,81,82,84]

>Chunk "U" (Chunk "PQRT" Empty)

Модули для работы со строками байтов содержат большое количество функций, аналогичных функциям в модуле >Data.List, включая следующие (но не ограничиваясь ими): >head, >tail, >init, >null, >length, >map, >reverse, >foldl, >foldr, >concat, >takeWhile, >filter и др.

Есть и функции, имя которых совпадает с именем функций из модуля >System.IO, и работают они аналогично, только строки заменены значениями типа >ByteString. Например, функция >readFile в модуле >System.IO имеет тип

>readFile :: FilePath –> IO String

а функция >readFile из модулей для строк байтов имеет тип

>readFile :: FilePath –> IO ByteString

ПРИМЕЧАНИЕ. Обратите внимание, что если вы используете строгие строки и выполняете чтение файла, он будет считан в память целиком! При использовании ленивых байтовых строк файл будет читаться аккуратными порциями.

Копирование файлов при помощи Bytestring

Давайте напишем простую программу, которая принимает два имени файла в командной строке и копирует первый файл во второй. Обратите внимание, что модуль >System.Directory уже содержит функцию >copyFile, но мы собираемся создать нашу собственную реализацию.

>import System.Environment

>import qualified Data.ByteString.Lazy as B


>main = do

>   (fileName1:fileName2:_) <– getArgs

>   copy fileName1 fileName2


>copy :: FilePath –> FilePath –> IO ()

>copy source dest = do

>   contents <– B.readFile source

>   bracketOnError

>      (openTemplFile "." "temp")

>      (\(tempName, tempHandle) -> do

>          hClose templHandle

>          removeFile tempName)

>      (\(tempName, tempHandle) -> do

>          B.hPutStr tempHandle contents

>          hClose tempHandle

>          renameFile tempName dest)

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

Сначала для чтения содержимого входного файла мы используем функцию >B.readFile. Затем с помощью >bracketOnError организуем обработку ошибок. Мы получаем ресурс посредством вызова >openTemplFile "." "temp", который возвращает пару из имени временного файла и его дескриптора. После этого указываем, что должно произойти при возникновении исключения. В этом случае мы закроем дескриптор и удалим временный файл. Наконец, выполняется собственно копирование. Для записи содержимого во временный файл используется функция