Вот снимок дорожной карты.
Строки и текст
Как вы, вероятно, знаете, тип 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
Data.ByteString.Char8
. Вы хотитеData.Text
при работе с текстом иData.ByteString
при работе с двоичными данными того или иного рода. Кроме того, преобразованиеString
вByteString
для эффективного письма не имеет особого смысла; Просто храните свои данные вByteString
в первую очередь, если это разумное представление. - person Cubic   schedule 07.09.2018ByteString
, ленивый и строгий. Они хранят необработанные байты, а не символы. ВариантыChar8
не являются отдельными типами; это просто модули, которые предоставляют немного другой интерфейс (эффективно обрабатываяByteString
как текст Latin-1).String
иText
хранят символы Unicode (т.е. текст). - person melpomene   schedule 08.09.2018