使用Data.Yaml解码含UTF-8的YAML文件时遇编码错误求助
你遇到的问题根源在于**Data.ByteString的IsString实例默认使用Latin-1编码,而非UTF-8**,导致你定义的ByteString并没有正确存储α的UTF-8字节序列。
问题分析
在GHCi中启用OverloadedStrings后,当你写let t :: ByteString = "α"时,bytestring包的IsString实现会把每个Char直接截断为8位(相当于Latin-1编码)。而α是Unicode字符U+03B1,它的编码超出了Latin-1的范围(U+0000到U+00FF),所以最终得到的ByteString只有一个字节0xB1(十进制177)——这个字节在UTF-8中属于续字节,不能单独作为首字节出现,因此YAML解析器直接抛出了"invalid leading UTF-8 octet"的错误。
你可以看到GHCi输出t为"\177",这正好验证了这一点:这个ByteString只包含错误的单字节,而非α正确的UTF-8双字节序列0xCE 0xB1(对应十进制206和177)。
解决方案
你需要确保ByteString存储的是正确的UTF-8编码字节,这里有几种简单的实现方式:
1. 使用text包的encodeUtf8函数
text包的字符串处理默认遵循UTF-8,我们可以先将字符串转为Text,再编码为UTF-8格式的ByteString:
λ> :m + Data.Text.Encoding λ> let t = encodeUtf8 "α" λ> t "\206\177" -- 这是α正确的UTF-8字节序列 λ> decodeEither' @Value t Right (String "α")
2. 使用bytestring-utf8包的UTF-8专用ByteString
bytestring-utf8包提供了专门处理UTF-8的ByteString类型,它的IsString实例会自动按UTF-8编码字符串:
λ> :m + Data.ByteString.UTF8 λ> let t :: ByteString = "α" λ> t "\206\177" λ> decodeEither' @Value t Right (String "α")
3. 手动构造正确的UTF-8字节序列
如果你不想引入额外依赖,也可以直接用pack构造包含正确UTF-8字节的ByteString:
λ> let t = pack [0xCE, 0xB1] λ> decodeEither' @Value t Right (String "α")
补充说明
你之前尝试的Data.ByteString.Char8本质上和Data.ByteString一样,都是按Latin-1处理字符,所以无法解决问题。而utf8-string包的UTF8类型需要你显式使用它的构造函数(比如fromString)才能得到正确的UTF-8编码,直接用类型声明可能无法触发正确的实例。
内容的提问来源于stack exchange,提问作者user3416536

