bytestrings 的控制台输出 - 删除下一行的第一个字符

Console Output of bytestrings - deletes first character of next line

回答 我发现 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"

我不明白的是 - 为什么第一行缺少 and 第二行打印的 p 是失踪。

我的猜测是,这与导致格式错误的输入的俄文字母有关。因为对于 "test" 的简单情况,它就可以正常工作。


编辑

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=

这不是终端问题,而是在转换为 ByteString 的早期出现问题。记住,因为你使用了 OverloadedStrings

inputB = "ДЕЖЗИЙКЛМНОПРСТУФ"

对于

来说真的是shorthand
inputB = fromString "ДЕЖЗИЙКЛМНОПРСТУФ"::ByteString

不使用 UTF8 转换为字节串。

如果您希望字节串包含 utf8 编码的字符,请使用

import qualified Data.ByteString.UTF8 as BU

inputB = BU.fromString "ДЕЖЗИЙКЛМНОПРСТУФ"

这样就可以了

B.putStrLn inputB

为什么第二行的 "p" 不见了?

我不会详细介绍(因为我不了解它们),但行为是预期的....因为您的终端需要 UTF8,而俄语字符串不是 UTF8。

UTF8 使用可变长度字节字符编码....根据 char 中的第一个字节,它可能需要更多。显然,俄语字符串中的最后一个字节开始了需要更多字节的 UTF8 编码,并且 "p" 被读入该字符。您的终端似乎只是忽略它无法打印的字符(我的终端打印垃圾),因此俄语字符串和下一个字符都丢失了。

你会注意到 "p" 在 xxd 输出中....终端只是认为它是未知字符的一部分而不打印它。

引用自Data.ByteString.Char8的文档 (强调我的)

Manipulate ByteStrings using Char operations. All Chars will be truncated to 8 bits. It can be expected that these functions will run at identical speeds to their Word8 equivalents in Data.ByteString.

More specifically these byte strings are taken to be in the subset of Unicode covered by code points 0-255. This covers Unicode Basic Latin, Latin-1 Supplement and C0+C1 Controls.

代码点 0x00-0xFF 中未分配西里尔字母,因此预计会出现编码问题。

我会推荐 against Data.ByteString.Char8 除非你正在处理纯 ASCII。即使 latin-1 编码的文本可能在某些环境中工作,latin-1 编码已经过时并且应该消亡。

要处理一般字符串,请改用 Data.Text。从 ByteStringText 的转换函数,反之亦然,are provided。当然,这些功能还得依赖于一些编码。