Консольный вывод байтовых строк - удаляет первый символ следующей строки
Отвечая на /questions/29315527/haskell-konvertiruet-yunikodnuyu-posledovatelnost-v-utf-8 я натолкнулся на странное поведение ByteString.putStrLn
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Text (Text)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B
inputB, inputB' :: ByteString
inputB = "ДЕЖЗИЙКЛМНОПРСТУФ"
inputB' = "test"
main :: IO ()
main = do putStr "B.putStrLn inputB: "; B.putStrLn inputB
putStr "print inputB: "; print inputB
putStr "B.putStrLn inputB': "; B.putStrLn inputB'
putStr "print inputB': "; print inputB'
который дает
B.putStrLn inputB:
rint inputB: "\DC4\NAK\SYN\ETB\CAN\EM\SUB\ESC\FS\GS\RS\US !\"#$"
B.putStrLn inputB': test
print inputB': "test"
то, что я не понимаю, здесь - почему отсутствует первая строка вывода и отсутствует буква p во второй строке.
Я предполагаю, что это как-то связано с русскими буквами, приводящими к неправильному вводу. Потому что с простым случаем "теста" это просто работает.
редактировать
- Платформа: Linux Mint 17.3
- кодировка файла: UTF-8
- терминал: гном-терминал /tmux/zsh
- GHC: 7,10,3
- стек: 1.0.4
выход xxd
> stack exec -- unicode | xxd
00000000: 422e 7075 7453 7472 4c6e 2069 6e70 7574 B.putStrLn input
00000010: 423a 2014 1516 1718 191a 1b1c 1d1e 1f20 B: ............
00000020: 2122 2324 0a70 7269 6e74 2069 6e70 7574 !"#$.print input
00000030: 423a 2022 5c44 4334 5c4e 414b 5c53 594e B: "\DC4\NAK\SYN
00000040: 5c45 5442 5c43 414e 5c45 4d5c 5355 425c \ETB\CAN\EM\SUB\
00000050: 4553 435c 4653 5c47 535c 5253 5c55 5320 ESC\FS\GS\RS\US
00000060: 215c 2223 2422 0a42 2e70 7574 5374 724c !\"#$".B.putStrL
00000070: 6e20 696e 7075 7442 273a 2074 6573 740a n inputB': test.
00000080: 7072 696e 7420 696e 7075 7442 273a 2022 print inputB': "
00000090: 7465 7374 220a test".
библиотеки
> stack exec -- ghc-pkg list
/opt/ghc/7.10.3/lib/ghc-7.10.3/package.conf.d
Cabal-1.22.5.0
array-0.5.1.0
base-4.8.2.0
bin-package-db-0.0.0.0
binary-0.7.5.0
bytestring-0.10.6.0
containers-0.5.6.2
deepseq-1.4.1.1
directory-1.2.2.0
filepath-1.4.0.0
ghc-7.10.3
ghc-prim-0.4.0.0
haskeline-0.7.2.1
hoopl-3.10.0.2
hpc-0.6.0.2
integer-gmp-1.0.0.0
pretty-1.1.2.0
process-1.2.3.0
rts-1.0
template-haskell-2.10.0.0
terminfo-0.4.0.1
time-1.5.0.1
transformers-0.4.2.0
unix-2.7.1.0
xhtml-3000.2.1
/home/epsilonhalbe/.stack/snapshots/x86_64-linux/lts-5.5/7.10.3/pkgdb
text-1.2.2.0
/home/epsilonhalbe/programming/unicode/.stack-work/install/x86_64-linux/lts-5.5/7.10.3/pkgdb
и язык
> locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=de_AT.UTF-8
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=de_AT.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=de_AT.UTF-8
LC_NAME=de_AT.UTF-8
LC_ADDRESS=de_AT.UTF-8
LC_TELEPHONE=de_AT.UTF-8
LC_MEASUREMENT=de_AT.UTF-8
LC_IDENTIFICATION=de_AT.UTF-8
LC_ALL=
2 ответа
Это не терминальная проблема, скорее, проблема возникает в начале преобразования в ByteString. Помните, потому что вы использовали OverloadedStrings
inputB = "ДЕЖЗИЙКЛМНОПРСТУФ"
действительно сокращение для
inputB = fromString "ДЕЖЗИЙКЛМНОПРСТУФ"::ByteString
который не конвертируется в строку байтов с использованием UTF8.
Если вместо этого вы хотите, чтобы строка байтов содержала символы в кодировке utf8, используйте
import qualified Data.ByteString.UTF8 as BU
inputB = BU.fromString "ДЕЖЗИЙКЛМНОПРСТУФ"
тогда это будет работать
B.putStrLn inputB
Почему "р" на второй строке отсутствует?
Я не буду вдаваться в подробности (потому что я их не знаю), но поведение ожидается... Потому что ваш терминал ожидает UTF8, а русская строка не UTF8.
UTF8 использует кодировки байтов переменной длины.... В зависимости от первого байта в символе, он может ожидать большего. Очевидно, что последний байт в русской строке запустил кодировку UTF8, для которой требовалось больше байтов, и в этот символ было прочитано "p". Ваш терминал, кажется, просто игнорирует символы, которые он не может распечатать (мой выводит мусор), поэтому и русская строка, и следующий символ были потеряны.
Вы заметите, что "р" находится в выводе xxd.... Терминал просто считает его частью неизвестных символов и не печатает его.
Цитирование из документации Data.ByteString.Char8
(акцент мой)
Управляйте ByteStrings, используя операции Char. Все символы будут усечены до 8 бит. Можно ожидать, что эти функции будут работать с той же скоростью, что и их эквиваленты в Word8 в Data.ByteString.
Более конкретно, эти байтовые строки считаются находящимися в подмножестве Unicode, покрытом кодовыми точками 0-255. Это охватывает Unicode Basic Latin, Latin-1 Supplement и C0+C1 Controls.
Кириллица не выделяется в кодовых точках 0x00-0xFF, поэтому следует ожидать проблем с кодированием.
Я бы рекомендовал против Data.ByteString.Char8
если вы не имеете дело с простым ASCII. Даже если текст в кодировке латинского алфавита 1 может работать в определенных условиях, кодировка латинского алфавита устарела и должна умереть.
Для обработки общих строк используйте Data.Text
вместо. Функции преобразования из ByteString
с Text
и наоборот, предоставляются. Конечно, эти функции должны зависеть от некоторой кодировки.