Windows 10 CLI UTF-8编码

3

问题:

在英语Windows 10上使用斯洛文尼亚键盘布局时,所有命令行界面似乎都存在显示(打印)UTF-8字符的问题,即č、š和ž这些字符被替换为?(我假设所有UTF-8特定字符也不起作用,因为ć和đ也不起作用。)

测试环境:

  • Windows 10 64位英语 - 斯洛文尼亚键盘布局下的CMD、Powershell、Cmder...无法成功
  • Windows 10 64位英语 - 斯洛文尼亚键盘布局下的Intellij IDEA...成功 -> 在IDE中按照需要工作,但在CLI中不起作用。
  • Windows 10 64位英语 - 英语键盘下的CMD...成功
  • Windows 10 64位斯洛文尼亚语 - 斯洛文尼亚键盘布局下的CMD...成功
  • 几个Linux发行版(Ubuntu、Mint、Kali)...成功

已尝试:

  • 将chcp更改为chcp 65001...无法成功
  • 在regedit中创建Autorun文件以强制使用UTF-8...无法成功
  • 不同的Java编译器...无法成功

示例代码:

public class Test2 {
public static void main(String[] args) {
    System.out.println("č š ž ć đ");

    }
}

CMD:

>javac -encoding UTF-8 test2.java
>java Test2
? ? ? ? ? 

其他注意事项:

该问题出现在多台运行不同硬件的计算机上。在所有上述提到的命令行界面中,所有上述提到的字符默认情况下都可以正常工作。因此,问题似乎只在Java中出现。


在英语Windows 10上使用斯洛文尼亚键盘布局。如果您阅读整个句子,您将获得您正在寻找的澄清。我很抱歉没有列出英语 - 英语和斯洛文尼亚语 - 斯洛文尼亚语作为操作系统和键盘布局的语言。因此,问题似乎只出现在运行英语语言且使用斯洛文尼亚键盘布局的Windows 10 64位上。另外,为了进一步澄清,我想补充说明,在使用IntelliJ IDEA IDE的Windows 10 64位英语语言、Slo.键盘布局上不会出现问题。 - user9420260
您的程序附加到一个控制台,该控制台可能是从shell继承而来的,但该控制台与CMD或PowerShell无直接关系。它不是“CMD窗口”。控制台系统使用主机进程(conhost.exe)的实例(Windows 7+)作为窗口,以及设备驱动程序(condrv.sys)(Windows 8+)作为ConDrv设备,提供控制台文件(参考、连接、输入、输出、当前输入、当前输出、控制台)。通常,控制台客户端具有Connect(通用控制台API)、Input(stdin)和Output(stdout、stderr)的句柄。 - Eryk Sun
控制台屏幕缓冲区是UCS-2 Unicode格式的,最好使用宽字符函数WriteConsoleW进行写入。传统程序使用WriteFileWriteConsoleA写入多字节字符串。在这种情况下,控制台使用其输出代码页(GetConsoleOutputCPSetConsoleOutputCP)来解码字符串。UTF-8作为代码页65001被支持,但根据Windows版本的不同,它极其容易出现错误。对于多字节输入(ReadFileReadConsoleA),包括Windows 10在内的所有版本都更糟糕,因为它无法读取除7位ASCII以外的任何内容。 - Eryk Sun
2个回答

5

使用 chcp 65001 命令,然后使用 java -Dfile.encoding=UTF-8 Test2 命令运行:

chcp 65001
javac -encoding UTF-8 Test2.java
java -Dfile.encoding=UTF-8 Test2

记得按照类名进行命名Java源文件,区分大小写。

可以了,谢谢。有没有办法强制执行-Dfile.encoding=UTF-8自动化? - user9420260
@user9420260 JVM如何分配默认编码是未记录的。它是实现特定的,所以我不能给你一个简单的答案。关于潜在的Linux答案,请参见Java VM如何确定其默认file.encoding? - Andreas
@user9420260 还可以参考这个答案:如何设置默认的Java字符编码? - Andreas
我不知道Java如何响应此问题,但在Windows 8之前(Windows 7仍然非常常见),向控制台写入UTF-8(代码页65001)通常会出现问题,因为WritFileWriteConsoleA报告编写的字节数错误; 它返回编写的解码后的UTF-16元素的数量。在这种情况下,使用缓冲流的C / C ++和其他语言运行时将自动尝试写入它们认为是剩余字节,并且这会导致每次打印后都会包含非ASCII字符的垃圾数据。 - Eryk Sun
此外,Windows 7 仍然默认使用 OEM 光栅字体,如果选择输出代码页为 65001,则即使是 WriteConsoleW(宽字符版本)也无法处理非 ASCII 文本。 - Eryk Sun

0

在遵循了@Andreas的建议后,我进一步探索了这个问题并找到了一个有效的解决方法:

首先,按照this link上超级用户的链接,强制cmd使用chcp 65001(UTF-8)。

其次,使用以下命令:

set JAVA_TOOL_OPTIONS =-Dfile.encoding=UTF-8

CMD使用控制台的宽字符函数ReadConsoleWWriteConsoleW来读写Unicode(UTF-16)。运行chcp.com 65001与在CMD中设置任何内容无关。您正在混淆一个使用控制台的shell和实际控制台。至于代码页65001,这是一个糟糕的解决方案。在Windows 7中它非常不稳定,即使在Windows 10中,您也无法读取非ASCII用户输入。这在非英语环境中会引起很大问题。如果Java没有更好的解决方案,那么它对Windows控制台的支持就是根本性的错误。 - Eryk Sun
@ErykSun Java与系统编码兼容良好。问题在于,Windows不使用Unicode作为默认编码(例如,在W10下)。在Java中编程可能会导致部署到默认使用Unicode的非Windows系统上。因此,使用UTF-8支持进行编译(如OP中所述)将意味着JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8。然而,我同意你的观点:在这里使用chcp 65001是没有用的。 - lauhub
@lauhub,自1993年起,控制台API在NT中就支持Unicode。Java可以支持宽字符函数,就像Python 3.6+一样。然而仍然存在问题。控制台是在Unicode扩展到基本多语言平面之前设计的,因此将UTF-16代理对处理为单个序数。此外,对于显示,它不支持字体回退和复杂脚本。为了解决这些问题,微软正在开发一个更新的终端程序,该程序使用控制台主机(conhost.exe)作为后端服务器,而不是将其用作UI客户端。 - Eryk Sun

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