在Windows下的Haskell中使用控制台I/O来处理Unicode字符似乎相当困难.这是悲惨的故事:
- (初步.)在您考虑在Windows下的控制台中进行Unicode I/O之前,您需要确保使用可以呈现所需字符的控制台字体.光栅字体(默认)具有无限的覆盖范围(并且不允许复制粘贴它们无法表示的字符),并且MS提供的truetype选项(consolas,lucida console)具有不太大的覆盖范围(尽管这些将允许复制/粘贴他们无法代表的字符).您可以考虑安装DejaVu Sans Mono(按照此处底部的说明进行操作;您可能必须在它运行之前重新启动).在对此进行排序之前,没有任何应用程序能够执行多少Unicode I/O; 不仅仅是哈斯克尔.
- 完成此操作后,您会注意到某些应用程序将能够在Windows下执行控制台I/O. 但让它发挥作用仍然非常复杂.基本上有两种方法可以在windows下写入控制台.(对于任何语言都是如此,不仅仅是Haskell;不要担心,Haskell会稍微进入图片!)......
- 选项A是使用通常的c库样式基于字节的i/o函数; 希望操作系统能够根据某些编码来解释这些字节,这些编码可以编码您想要的所有奇怪和精彩的字符.例如,在Mac OS X上使用等效技术,标准系统编码通常是UTF8,这很好用; 你发出utf8输出,你会看到漂亮的符号.
- 在Windows上,它的效果不太好.Windows期望的默认编码通常不是涵盖所有Unicode符号的编码.因此,如果您希望以这种方式看到漂亮的符号,您需要更改编码.一种可能性是您的程序使用
SetConsoleCPwin32命令.(那么你需要绑定到Win32库.)或者,如果您不想这样做,您可以期望程序的用户为您更改代码页(然后他们必须chcp在运行您之前调用命令程序).
- 选项B是使用支持Unicode的win32控制台API命令
WriteConsoleW.在这里,您可以直接向窗口发送UTF16,这使得它很愉快:没有编码不匹配的危险,因为Windows 总是希望UTF16具有这些功能.
不幸的是,这些选项都不适用于Haskell.首先,我知道没有使用选项B的库,所以这并不容易.这留下了选项A.如果你使用Haskell的I/O库(putStrLn等等),这就是库的功能.在现代版本的Haskell中,它会仔细询问windows当前代码页是什么,并以正确的编码输出字符串.这种方法存在两个问题:
- 一个不是一个showstopper,但很烦人.如上所述,默认编码几乎不会编码您想要的字符:您是用户需要更改为编码的编码.因此,您的用户需要
chcp cp65001在他们运行您的程序之前(您可能会发现强迫您的用户执行此操作令人反感).或者你需要绑定SetConsoleCP并在程序中执行等效操作(然后使用hSetEncoding以便Haskell库使用新编码发送输出),这意味着你需要包装win32库的相关部分以使它们可见Haskell .
- 更严重的是,Windows中存在一个错误(解决方案:无法修复)导致Haskell中的错误,这意味着如果您选择了可以覆盖所有Unicode的任何代码页,如cp65001,Haskell的I/O例程将故障和失败.所以基本上,即使你(或你的用户)正确地将编码设置为覆盖所有精彩Unicode字符的某些编码,然后在"告诉Haskell使用该编码输出内容时做一切正确",你仍然会丢失.
上面列出的错误仍未解决,并列为低优先级; 基本结论是选项A(在我上面的分类中)是不可行的,需要切换到选项B以获得可靠的结果.目前尚不清楚解决这个问题的时间表是什么,因为它看起来像是一些相当大的工作.
问题是:与此同时,任何人都可以建议一种解决方法,允许在Windows下的Haskell中使用Unicode控制台I/O.
另请参阅此python错误跟踪器数据库条目,解决Python 3中的相同问题(已提出修复,但尚未被接受到代码库中),以及此stackoverflow答案,为Python中的此问题提供了一种解决方法(基于'选项B'在我的分类中).