Кодирование и эффективный ввод-вывод в Haskell - PullRequest
0 голосов
/ 07 сентября 2018

Здравствуйте, я немного запутался во всех модулях Haskell, необходимых для кодирования данных от String до ByteString для эффективной записи.

Я не понимаю, как вы конвертируете Data.ByteString.Lazy в Data.ByteString.Char8 и наоборот.

Что мне нужно знать? Потому что я не могу получить все эти возможные комбинации употреблений .... Data.ByteString, Data.ByteString.Lazy, Data.ByteString.Char8, тогда есть Data.Text ..... что мне нужно для того, чтобы легко и эффективно записывать строки в файлы и наоборот? (с правильной кодировкой)

P.S В настоящее время читаю Real World Haskell, и я запутался во всех этих модулях.

Ответы [ 2 ]

0 голосов
/ 08 сентября 2018

Это зависит от типа данных, с которыми вы имеете дело, и от того, как вы планируете передавать эти данные.

Если вы имеете дело со строками Unicode, используйте Text из текстового пакета.

Если вам не нужно считывать все данные в память сразу, используйте версию Lazy этого модуля. В противном случае все данные будут загружены в одну структуру данных.

Когда использовать Data.ByteString или Data.ByteString.Char8, зависит от того, что вы хотите, чтобы эта строка байтов представляла: последовательность байтов или последовательность 8-битных символов. ByteString - это структура данных, которую вы можете использовать для хранения либо последовательности байтов, каждый из которых имеет тип: Word8, либо последовательности 8-битных символов, каждый из которых имеет тип: Char. Существует только один тип ByteString.

Поскольку часто мы можем иметь дело с двоичными данными, смешанными с символьными данными, было бы удобно, если бы мы хранили операции для байтов и символов отдельно в разных модулях; таким образом, когда нам нужно обрабатывать символьные данные, просто используйте операции из модулей Char8.

0 голосов
/ 08 сентября 2018

Вот пример дорожной карты.

Строки и текст

Как вы, вероятно, знаете, тип Haskell String - это просто синоним типа для [Char], где Char - это тип данных, который может представлять одну кодовую точку Unicode. Это делает String идеальным типом данных для представления текстовых данных, за исключением незначительной проблемы, которая - в виде связанного списка в штучной упаковке Char значений - потенциально может быть крайне неэффективной.

Тип данных Text из пакета text решает эту проблему. Text также, как и String, представляет собой список значений Char, но вместо использования фактического списка на Haskell он использует представление с эффективным использованием времени и пространства. Это должна быть ваша замена String, когда вам нужно эффективно работать с текстовыми (Unicode) данными.

Как и многие другие типы данных в стандартных библиотеках Haskell, он поставляется в ленивых и строгих вариантах. Оба варианта имеют одинаковое имя Text, но они содержатся в отдельных модулях, поэтому вы можете сделать следующее:

import qualified Data.Text as TS
import qualified Data.Text.Lazy as TL

, если вам нужно было использовать оба варианта TS.Text и TL.Text в одной и той же программе.

Точная разница между вариантами описана в документации для Data.Text . В двух словах, вы должны по умолчанию использовать строгую версию. Вы используете ленивую версию только в двух случаях. Во-первых, если вы планируете работать с большим значением Text постепенно, рассматривая его скорее как текстовый «поток», чем «строку», тогда ленивая версия - хороший выбор. (Например, программа для чтения огромного числового CSV-файла может считывать файл в виде длинного ленивого потока Text и сохранять результаты в эффективном числовом типе, таком как Vector из неупакованных значений Double, чтобы избежать сохранения весь вводимый текст в памяти.) Во-вторых, если вы строите большую строку Text из множества маленьких кусочков, то вы не хотите использовать строгие версии, потому что их неизменность означает, что их нужно копировать всякий раз, когда вы Добавьте что-нибудь. Вместо этого вы хотели бы использовать ленивый вариант с функциями из Data.Text.Lazy.Builder.

1038 * байтовые строки * Тип данных ByteString из пакета bytestring, с другой стороны, является эффективным представлением списка байтов. Точно так же, как Text является эффективной версией [Char], вы должны рассматривать ByteString как эффективную версию [Word8], где Word8 - это тип Haskell, представляющий один беззнаковый байт данных со значением 0-255 , Эквивалентно, вы можете думать о ByteString как о представлении фрагмента памяти или фрагмента данных, которые должны быть считаны или записаны в файл, точно как байты для байтов. Это также входит в ленивые и строгие ароматы: import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL , и соображения по использованию вариантов аналогичны тем, что указаны для Text. Чтение и запись в файлы

В программе на Haskell обычно строки Unicode обычно представляются в виде значений String или Text. Однако, чтобы считать их из файлов или записать их в файлы, их необходимо кодировать и декодировать из последовательностей байтов.

Самый простой способ справиться с этим - использовать функции Haskell, которые автоматически обрабатывают кодирование и декодирование. Как вы, наверное, знаете, в Prelude уже есть две функции, которые читают и пишут строки напрямую:

readFile :: FilePath -> IO String
writeFile :: FilePath -> String -> IO ()

Кроме того, в text есть функции readFile и writeFile, которые делают это. Вы можете найти версии как в Data.Text.IO, так и в Data.Text.Lazy.IO. Кажется, они имеют одинаковые подписи, но одна работает на строгом типе Text, а другая - на ленивом Text типе:

readFile :: FilePath -> IO Text
writeFile :: FilePath -> Text -> IO ()

Вы можете сказать, что эти функции выполняют кодирование и декодирование автоматически, потому что они возвращают и принимают значения Text, а не ByteString. Используемая кодировка по умолчанию будет зависеть от операционной системы и ее конфигурации. В типичном современном дистрибутиве Linux это будет UTF-8.

Кроме того, вы можете читать или записывать необработанные байты из файла, используя функции из пакета bytestring (опять же, либо ленивые, либо строгие версии, в зависимости от модуля):

readFile :: FilePath -> IO ByteString
writeFile :: FilePath -> ByteString -> IO ()

Они имеют те же имена, что и версии text, но вы можете видеть, что они имеют дело с необработанными байтами, потому что они возвращают и принимают ByteString аргументы. В этом случае, если вы хотите использовать эти ByteString в качестве текстовых данных, вам нужно будет их самостоятельно декодировать или кодировать. Если, например, ByteString представляет версию текста в кодировке UTF-8, то вам нужны следующие функции: Data.Text.Encoding (для строгих версий) или Data.Text.Lazy.Encoding (для ленивых версий):

decodeUtf8 :: ByteString -> Text
encodeUtf8 :: Text -> ByteString

Модули Char8

Теперь модули в Data.ByteString.Char8 и Data.ByteString.Lazy.Char8 являются особым случаем. Когда простой текст ASCII был закодирован с использованием одной из нескольких схем кодирования, сохраняющих ASCII (включая сам код ASCII, кодирование Latin-1 и другие символы Latin-x и UTF-8), оказывается, что кодированный ByteString является просто простое однобайтовое кодирование кодовых точек Unicode от 0 до 127. Чуть более широко, когда текст был закодирован в Latin-1, тогда кодированный ByteString представляет собой простое однобайтовое кодирование символов кодовых точек Unicode от 0 до 255. В этих случаях и только в этих случаях функции в этих модулях можно безопасно использовать для обхода явных шагов кодирования и декодирования и просто обрабатывать строку байтов как текст ASCII и / или Latin-1 непосредственно путем автоматического преобразования отдельных байтов в значения Unicode Char и обратно.

Поскольку эти функции работают только в этом особом случае, обычно следует избегать их использования, кроме как в специализированных приложениях.

Кроме того, как было отмечено в комментарии, варианты ByteString в этих Char8 модулях ничем не отличаются от простых строгих и ленивых ByteString вариантов; просто обрабатываются так, как если бы они представляли собой строки Char значений вместо Word8 значений функций в этих модулях - типы данных одинаковы, только интерфейс функции отличается.

Общая стратегия

Итак, если вы работаете с обычным текстом и кодировкой вашей операционной системы по умолчанию, просто используйте строгий тип данных Text из Data.Text и (высокоэффективные) функции ввода-вывода из Data.Text.IO. Вы можете использовать ленивые варианты для потоковой обработки или построения больших строк из крошечных кусочков, и вы можете использовать Data.Text.Read для простого анализа.

Вы должны быть в состоянии вообще избегать использования String в большинстве ситуаций, но если вы обнаружите, что вам нужно конвертировать туда и обратно, тогда эти функции преобразования в Data.Text (или Data.Text.Lazy) будут полезны:

pack :: String -> Text
unpack :: Text -> String

Если вам нужен больший контроль над кодировкой, вы все еще хотите использовать Text во всей вашей программе, за исключением "краев", где вы читаете или пишете файлы. На этих границах используйте функции ввода / вывода из Data.ByteString (или Data.ByteString.Lazy) и функции кодирования / декодирования из Data.Text.Encoding или Data.Text.Lazy.Encoding.

Если вам нужно смешать строгие и ленивые варианты, обратите внимание, что Data.Text.Lazy содержит:

toStrict :: TL.Text -> TS.Text     -- convert lazy to strict
fromStrict :: TS.Text -> TL.Text   -- vice versa

и Data.ByteString.Lazy содержат соответствующие функции для значений ByteString:

toStrict :: BL.ByteString -> BS.ByteString
fromStrict :: BS.ByteString -> BL.ByteString
...