You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Data.Text序列化至ByteString时多余\NUL与\EOT字节的问题解析

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"

疑问

  1. 为何会生成这些\NUL和\EOT字节?
  2. 如何在最终的ByteString中避免它们?

补充说明

实际代码中会手动在文本前添加长度信息:

foo :: Text -> ByteString
foo txt = runPut do
    putWord32host $ T.length txt
    put txt

问题解答

1. 多余字节的成因

Data.Binary库中Text类型的Binary实例,默认序列化逻辑是先写入一个64位大端格式的长度值,再写入文本的UTF-8编码内容。

对于长度为4的文本"Text",64位大端的长度值是0x0000000000000004,对应ASCII字符就是7个\NUL(空字节,ASCII码0)加1个\EOT(传输结束符,ASCII码4),这就是你看到的前缀字节。这个前缀是用来在反序列化时告诉程序需要读取多少字节的文本内容。

2. 避免多余字节的方案

既然你已经手动添加了32位的长度前缀,就不需要Binary自动生成的64位长度前缀了。直接将Text转成UTF-8编码的ByteString,再写入即可:

修改后的代码如下:

import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import Data.Binary
import Data.Binary.Put
import Data.Word

foo :: T.Text -> ByteString
foo txt = runPut $ do
    putWord32host $ fromIntegral $ T.length txt
    putByteString $ TE.encodeUtf8 txt

说明

  • TE.encodeUtf8直接将Text转换为UTF-8编码的原始ByteString,不带任何额外前缀;
  • putByteString负责将这个原始字节流写入序列化器,最终生成的结果就是你手动添加的32位长度,加上文本的UTF-8字节,不会有多余的\NUL和\EOT。

注意:如果你的场景需要记录的是文本的字节数而非字符数,需要把T.length txt替换为BS.length $ TE.encodeUtf8 txt(需导入qualified Data.ByteString as BS)。


内容的提问来源于stack exchange,提问作者Jogger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:05:21