我在 Windows 控制台中打印 unicode 符号时遇到问题。
这里是打印出unicode符号值的java代码;
System.out.print("\u22A2 ");
当我在 Eclipse 中使用编码设置为 UTF-8 运行程序时,问题不存在,但是当涉及到 Windows 控制台时,符号会被问号替换。
尝试了以下方法来解决这个问题,但没有成功;
将 Windows 控制台的字体更改为 Lucida Console。
每次运行 Windows 控制台时,我都会更改编码设置,即使用
chcp 65001
我尝试过几次的额外步骤是使用参数运行 java 文件,即
java -Dfile.encoding=UTF-8 Filter
(其中“Filter”是类的名称)
默认情况下,Windows 的CMD中使用的代码页是437。您可以通过在提示符下运行此命令进行测试:
C:\>chcp
Active code page: 437
并且,此代码页会阻止您正确显示 Unicode 字符!为此,您必须将代码页更改为 65001 并使用 -Dfile.encoding=UTF-8。
C:\>chcp 65001
Active code page: 65001
C:\>java -jar -Dfile.encoding=UTF-8 path/to/your/runnable/jar
除了您已采取的步骤之外,您还需要一个将打印字符编码为 UTF-8 的 PrintStream/PrintWriter。
不幸的是,Java 设计者选择使用所谓的“默认”编码来打开标准流,这在 Windows 下几乎总是无法使用*)。因此,天真地使用
System.out
和 System.err
会让你的程序输出看起来不同,具体取决于你运行它的位置。这直接违背了目标:编译一次,随处运行。
*) 这将是一些非标准的“代码页”,除了微软之外,在这个星球上没有人认识到。 AFAIK,例如,如果您有一个德语键盘和一个“德语”OEM Windows,并且您希望在您的本地时区中获得日期和时间,则无法说:但我想要 UTF-8 输入/输出我的 CMD 窗口。这就是为什么我大部分时间都启动双 Ubuntu 的原因之一,不言而喻,终端支持 UTF-8。
以下内容通常在 JDK7 中适用于我:
public static PrintWriter stdout = new PrintWriter(
new OutputStreamWriter(System.out, StandardCharsets.UTF_8),
true);
对于古老的 Java 版本,我将
StandardCharsets.UTF_8
替换为 Charset.forName("UTF-8")
我已经为同样的问题苦苦挣扎了很长时间,但我相信我终于找到了一个(如果不是很漂亮的话)解决方案。
据我所知,问题实际上由2个问题组成:
System.out
默认使用不正确的编码第一个问题可以使用
cmd
或 powershell
并运行 chcp
来观察:
Active code page: 850.
对于 UTF-8,这应该是
65001
,可以使用 chcp 65001
设置。不过,只有当您可以在程序运行的外壳中运行命令,或者编辑注册表自动运行字段(在我看来,这两者都不是很好的选择)时,这才有效。
不,您无法运行 Runtime.getRuntime().exec("chcp.com 65001")
,因为这不会影响调用控制台,而只会影响通过运行命令创建的控制台。
我的建议是使用本机 Windows 函数 SetConsoleOutputCP(),这意味着您可以从内部更改代码页并为您的应用程序隔离。我只是使用了 JNA,但编写一些本机 C 包装器可能会更清晰,这样您就只能获得一个函数:
Kernel32.INSTANCE.SetConsoleOutputCP(65001)
我在打印时发现这个问题
System.getProperties()
:
...
stdout.encoding=Cp1252
...
与实际编码不同(至少对我来说,是
850
),而不是 UTF-8(请注意,这是使用 Java 21 进行测试的,它显然默认情况下使用 UTF-8,但显然不是在所有地方)。
同样,这可能可以通过添加一些启动参数来设置这些属性来解决,但您也可以创建自己的打印流:
new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8)
您可以使用
System.setOut()
将其设置为全局 System.out。
这是我以独立于平台的方式修复 System.out 和 System.err 的建议:
public static void fixSystemOutEncoding() {
if(
System.console() == null || // No interactive terminal connected (maybe you still want to do it there?)
!System.getProperty("os.name").toLowerCase().contains("win") || // Not on Windows
System.getProperty("jdk.console", "").toLowerCase().contains("jshell") // Is JShell session. In my experience, the modified output stream doesn't work there
) {
// No console, no Windows, JShell -> nothing to do
return;
}
try {
// Set console code page to 65001 = UTF-8
if(Kernel32.INSTANCE.SetConsoleOutputCP(65001)) {
// Replace System.out and System.err with PrintStreams using UTF-8
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8));
System.setErr(new PrintStream(new FileOutputStream(FileDescriptor.err), true, StandardCharsets.UTF_8);
}
else {
// SetConsoleOutputCP() failed, throw exception with error message,
// handle it in catch (you may want to do something else here or
// just ignore it)
throw new RuntimeException(Kernel32Util.getLastErrorMessage());
}
} catch(Throwable t) {
// Something went wrong, probably with the native library
// Probably just ignore it and deal with UTF-8 not being available
}
}
需要 JNA 和 JNA 平台 (
net.java.dev.jna:jna-platform
)。
对于阿拉伯语,我使用了以下代码:
PrintWriter stdout = new PrintWriter(
new OutputStreamWriter(System.out,StandardCharsets.ISO_8859_1),true);