从Scala解释器打印Unicode

21

使用Scala解释器(即在命令行上运行“scala”命令)时,我无法正确打印Unicode字符。当然,a-z、A-Z等可以正确打印,但例如 € 或 ƒ 将打印为 ?。

print(8364.toChar)

输出结果显示的不是 € 而是 ?,可能是我做错了什么。我的终端支持 utf-8 字符集,即使将输出导入到另一个文件并在文本编辑器中打开,也会显示 ?。

这一切发生在 Mac OS X(Snow Leopard,10.6.2)上,使用 Scala 2.8(夜间构建版本)和 Java 1.6.0_17。


你在哪个操作系统上运行解释器?使用的是哪个版本的Scala? - Daniel C. Sobral
3个回答

18

我找到了问题的原因,并提供了一种解决方案,使它能够正常工作。正如我在发布问题后猜测并阅读Calum的答案以及另一个Java项目在Mac上出现编码问题一样,问题的根源是Mac OS X使用的默认编码。当您启动scala解释器时,它会使用指定平台的默认编码。在Mac OS X上,这是Macroman,在Windows上可能是CP1252。您可以通过在scala解释器中键入以下命令来检查:

scala> System.getProperty("file.encoding");
res3: java.lang.String = MacRoman
根据 scala 帮助测试,可以使用 -D 选项提供 Java 属性。然而,这对我不起作用。最终我设置了环境变量。
JAVA_OPTS="-Dfile.encoding=UTF-8"

运行scala后,上一个命令的结果将会得到以下结果:

scala> System.getProperty("file.encoding")
res0: java.lang.String = UTF-8

现在,打印特殊字符按预期工作:

print(0x20AC.toChar)               
€

所以,这不是Scala的bug,而是默认编码的问题。在我看来,默认情况下在所有平台上使用UTF-8会更好。在寻找答案时,我在Scala邮件列表中的讨论中发现了这个问题。在第一条消息中,建议在file.encoding报告Macroman时默认使用UTF-8,因为UTF-8是Mac OS X上的默认字符集(让我想知道为什么file.encoding默认设置为Macroman,可能这是从10之前的Mac OS继承而来?)。我认为这个提议不会成为Scala 2.8的一部分,因为Martin Odersky 写道,最好保持Java中的现状(即尊重file.encoding属性)。


1
一种避免McDowell所指出问题的方法是,将System.out PrintStream(仍然作为原始OutputStream工作)包装在使用所需编码的PrintStream中,然后使用它,例如“val myOut = new PrintStream(System.out,“UTF-8”); myOut.print(0x20AC.toChar)”。这应该总是有效的。 - Calum
1
@Calum - 很有趣的是看看它是否适用于Mac;在Windows上它的效果不是很好,但这可能是一个特定于平台的问题:http://illegalargumentexception.blogspot.com/2009/04/i18n-unicode-at-windows-command-prompt.html#charsets_javaconsole - McDowell
1
谢谢,我也在想,你的方法很有效。记录一下,在Windows上,我们必须使用set JAVA_OPTS=-Dfile.encoding=UTF-8(不带引号)。重定向到文件中,因为cmd.exe使用OEM编码以与MS-Dos兼容,我想。 - PhiLho
在过去的两年中,可能发生了一些变化,以至于“scala -Dfile.encoding=UTF-8”可以在我的机器上运行。无需设置JAVA_OPTS环境变量。 - vesan
1
无法在Windows上运行。(0x20AC until 0x20B6).foreach { x => print(x.toChar + " ") } 输出 Γé¼ Γé¡ Γé« Γé» Γé░ Γé▒ Γé▓ Γé│ Γé┤ Γé╡。我使用 System.getProperty("file.encoding") 设置了返回 UTF-8 的环境变量。 - Jus12
显示剩余4条评论

3

好的,你在这里遇到的问题至少部分是因为128不是欧元符号的Unicode代码点。128(或0x80,因为十六进制似乎是一般情况)是U+0080 <control>,即它不是可打印字符,所以你的终端难以显示它并不令人惊讶。

欧元符号的代码点是0x20AC(或者十进制8364),对我而言它可以工作(我使用的是Linux,2.8版本的夜间版):

scala> print(0x20AC.toChar)
€

另一个有趣的测试是打印Unicode雪人字符:

scala> print(0x2603.toChar)
☃

据说,128作为欧元符号是从Windows代码页中的一个扩展字符得到的。

我也成功让你提到的另一个字符工作了:

scala> 'ƒ'.toInt
res8: Int = 402

scala> 402.toChar
res9: Char = ƒ

关于欧元符号错误的数字,您是正确的。然而,在我的系统中它仍然无法正常工作: scala> print(0x20AC.toChar) ?但如果在您的夜间构建中可以正常工作,那么这可能是我的系统存在问题,或者它已经在新的Scala 2.8版本中得到修复。我会进行更新和进一步调查。 - Martin Sturm
我在今天的夜间版本(2.8.0.r20300-b20091223020158)上进行了检查,'print(0x20AC.toChar)' 像我手头其他所有 2.8 版本一样打印出一个问号。 - p3t0r
顺便说一下,我正在使用OSX 10.6.2操作系统。 - p3t0r
啊,我明白了,这是来自其他回答的一个文件编码问题。抱歉!请看一下你接受的答案,马丁,我在评论里有提到。 - Calum

1

对于Windows命令行(cmd)打印:

  1. set JAVA_OPTS="-Dfile.encoding=UTF-8"
  2. chcp 65001

第2项表示UTF-8

如果您不想每次都打印“chcp 65001”,可以像这样更改/添加Windows注册表中的值:

  1. 运行命令regedit
  2. 找到记录[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
  3. 新建 => 字符串值
  4. 名称=“AutoRun”,数据=“chcp 65001”(不带引号)

(请参见https://superuser.com/a/482117/454417

我使用Windows 10和scala 2.11.8


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