Кодирование и эффективный ввод-вывод в Haskell

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

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

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

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


person Bercovici Adrian    schedule 07.09.2018    source источник
comment
Обычно вам вообще не нужен Data.ByteString.Char8. Вы хотите Data.Text при работе с текстом и Data.ByteString при работе с двоичными данными того или иного рода. Кроме того, преобразование String в ByteString для эффективного письма не имеет особого смысла; Просто храните свои данные в ByteString в первую очередь, если это разумное представление.   -  person Cubic    schedule 07.09.2018
comment
Есть два типа ByteString, ленивый и строгий. Они хранят необработанные байты, а не символы. Варианты Char8 не являются отдельными типами; это просто модули, которые предоставляют немного другой интерфейс (эффективно обрабатывая ByteString как текст Latin-1). String и Text хранят символы Unicode (т.е. текст).   -  person melpomene    schedule 08.09.2018


Ответы (2)


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

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

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

байтовые строки

С другой стороны, тип данных 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
person K. A. Buhr    schedule 07.09.2018
comment
Но как преобразовать из ByteString в Text и наоборот? - person Bercovici Adrian; 08.09.2018
comment
Я обновил конец раздела Reading and Writing Files, чтобы сделать его более понятным. Ответ заключается в том, что используемые функции преобразования зависят от кодировки, но ЕСЛИ ByteString — это версия текста в кодировке UTF-8, тогда encodeUtf8 и decodeUtf8 из Data.Text.Encoding — это функции преобразования, которые вам нужны. - person K. A. Buhr; 08.09.2018

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

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

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

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

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

person user12457    schedule 07.09.2018