将 Data.Text 值序列化为 ByteString,不含不必要的 \NUL 字节

Jog*_*ger 6 haskell bytestring

使用以下代码,我想将 Data.Text 值序列化为 ByteString。不幸的是,我的文本前面添加了不必要的 NUL 字节和 EOT 字节:

GHCi, version 9.4.4: https://www.haskell.org/ghc/  :? for help
ghci> import qualified Data.Text as T
ghci> import Data.Binary
ghci> import Data.Binary.Put
ghci> let txt = T.pack "Text"
ghci> runPut $ put txt
"\NUL\NUL\NUL\NUL\NUL\NUL\NUL\EOTText"
ghci>
Run Code Online (Sandbox Code Playgroud)

问题:

  • 为什么会生成这些 NUL 和 EOT 字节?
  • 如何在生成的 ByteString 中避免它们?

PS:我真正的代码我把长度放在文本前面

    foo :: Text -> ByteString
    foo txt = runPut do
        putWord32host $ T.length txt
        put txt
Run Code Online (Sandbox Code Playgroud)

Wil*_*sem 5

它实际上已经对二进制字符串的长度进行了编码。事实上,如果我们查看源代码,对于Text的实例Binary,我们会看到\xc2\xa0 [src]

\n
\n
instance Binary Text where\n    put t = put (encodeUtf8 t)\n    get   = do\n      bs <- get\n      case decodeUtf8\' bs of\n        P.Left exn -> P.fail (P.show exn)\n        P.Right a -> P.return a
Run Code Online (Sandbox Code Playgroud)\n
\n

这并不奇怪,我们将其编码为 UTF-8,生成一个ByteString,然后使用put它。但长度是在我们本身的时候加上putByteString。事实上,BinaryString实例Binary看起来像\xc2\xa0 [src]

\n
\n
instance Binary B.ByteString where\n    put bs = put (B.length bs)\n             <> putByteString bs\n    get    = get >>= getByteString
Run Code Online (Sandbox Code Playgroud)\n
\n

因此put,产生ByteString的 写入encodeUtf8八个字节来指定 的大小,因此这是字节ByteString数,而不是(本身与)中的字符数Text

\n

如果您想要相同的效果,但没有长度前缀,您可以使用:

\n
import Data.Text.Encoding\n\nrunPut (putByteString (encodeUtf8 txt))\n
Run Code Online (Sandbox Code Playgroud)\n

因此,这省略了长度标头。

\n