GHC/Haskell如何决定从哪种字符编码进行解码/编码?

12

看起来 GHC 在决定解码字符编码方面至少是不一致的。

考虑一个名为 omatase-shimashita.txt 的文件,其内容如下,以 UTF-8 编码:お待たせしました。

readFile 似乎可以正确读取这个文件...

Prelude> content <- readFile "/home/chris/Desktop/omatase-shimashita.txt"
Prelude> length content
8
Prelude> putStrLn content
お待たせしました

然而,如果我写一个简单的“echo”服务器,它默认不会解码为UTF-8。考虑下面处理传入客户端的代码:

handleClient handle = do
  line <- hGetLine handle
  putStrLn $ "Read following line: " ++ toString line
  handleClient handle

并且相关的客户端代码,明确地发送 UTF-8:

Data.ByteString.hPutStrLn handle $ Codec.Binary.UTF8.Generic.fromString "お待たせしました"

这不是一致的行为吗?这背后有什么方法吗?我计划重写我的应用程序,明确使用 ByteString 对象并使用 Codec.Binary.UTF8 明确地进行编码和解码,但无论如何了解这里发生了什么都是好的... :o/

更新:我正在运行 Ubuntu Linux 版本 10.10,区域设置为 en_US.UTF-8...

$ cat /etc/default/locale 
LANG="en_US.UTF-8"
$ echo $LANG 
en_US.UTF-8

最终我使用了 Codec.Binary.UTF8 进行编码/解码,使用 Data.ByteString 发送/接收原始字节。据推测,chrisdb的解决方案应该是可行的;对我来说,在我测试我的玩具服务器/客户端时它起作用了,但在我尝试在我的真实应用程序上运行时却没有起作用(这可能是由于我错过了一些细节,但我变得不耐烦并采用了 ByteString 方法)。 - Chris W.
2个回答

6
你使用的GHC版本是哪个?旧版本特别不擅长处理Unicode I/O。
GHC文档中的这一部分描述了如何更改输入/输出编码:http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/System-IO.html#23 此外,文档还说:
文本模式的Handle有一个相关的TextEncoding,用于在读取时将字节解码为Unicode字符,并在写入时将Unicode字符编码为字节。
默认的TextEncoding与您系统上的默认编码相同,也可以作为localeEncoding使用。(GHC注意:在Windows上,我们目前不支持双字节编码;如果控制台的代码页不受支持,则localeEncoding将为latin1。)
始终检测和报告编码和解码错误,除了在惰性I/O(hGetContents、getContents和readFile)期间,其中解码错误仅导致字符流终止,就像其他I/O错误一样。
也许这与你的问题有关?如果GHC在某个地方默认使用了除utf-8以外的其他编码,或者你的handle已手动设置为使用不同的编码,那么这可能会解释问题。如果你只是尝试在控制台上回显文本,那么可能会出现某种控制台代码页的问题。我知道我以前在其他语言(如Python)中也遇到过类似的问题,在Windows控制台中打印Unicode字符。
尝试运行hSetEncoding handle utf8,看看是否可以解决你的问题。

总结:如果你正在向控制台/stdout输出,那么可能与你的系统默认编码有关,或者GHC在Windows上默认为latin1。 - chrisdb
FYI,这不是Windows的问题,我也不认为这是终端特定的问题。我正在Ubuntu上运行,本地化设置为en_US.UTF-8。cat /etc/default/locale输出LANG="en_US.UTF-8",而echo $LANG输出en_US.UTF-8 - Chris W.

6
您的第一个示例使用标准IO库System.IO。该库中的操作使用默认系统编码(也称为localeEncoding),除非您另有规定。假设您的系统设置为使用UTF-8,因此putStrLnhGetContents等都使用UTF-8编码。
您的第二个示例使用Data.ByteString。由于该库仅处理字节序列,因此不进行编码或解码。因此,Data.ByteString.hGetLine将文件中的字节直接转换为ByteString
一般情况下,进行文本I/O的最佳方式是使用text包。

是的,我理解标准字符StringByteString之间的区别... 我只是想指出我肯定在通过网络发送UTF-8编码的文本--但是,由于某种原因,接收方(使用System.IO.hGetLine)没有使用UTF-8进行解码。 - Chris W.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接