Копирование файлов при помощи 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", который возвращает пару из имени временного файла и его дескриптора. После этого указываем, что должно произойти при возникновении исключения. В этом случае мы закроем дескриптор и удалим временный файл. Наконец, выполняется собственно копирование. Для записи содержимого во временный файл используется функция B.hPutStr. Временный файл закрывается, и ему даётся имя, которое он должен иметь в итоге.
Заметьте, что мы использовали B.readFile и B.hPutStr вместо их обычных версий. Для открытия, закрытия и переименования файлов специальные функции не требуются. Они нужны только для чтения и записи.
Проверим программу:
$ ./bytestringcopy bart.txt bort.txt
Обратите внимание, что программа, не использующая строки байтов, могла бы выглядеть точно так же. Единственное отличие – то, что мы используем B.readFile и B.hPutStr вместо readFile и hPutStr. Во многих случаях вы можете «переориентировать» программу, использующую обычные строки, на использование строк байтов, просто импортировав нужные модули и проставив имя модуля перед некоторыми функциями. В ряде случаев вам придётся конвертировать свои собственные функции для использования строк байтов, но это несложно.
Если вы хотите улучшить производительность программы, которая считывает много данных в строки, попробуйте использовать строки байтов; скорее всего, вы добьётесь значительного улучшения производительности, затратив совсем немного усилий. Обычно я пишу программы, используя обычные строки, а затем переделываю их на использование строк байтов, если производительность меня не устраивает.