在Windows控制台中从Java代码问题打印出unicode

问题描述 投票:0回答:4

我在 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”是类的名称)

java unicode encoding utf-8 cmd
4个回答
9
投票

默认情况下,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

6
投票

除了您已采取的步骤之外,您还需要一个将打印字符编码为 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")


0
投票

我已经为同样的问题苦苦挣扎了很长时间,但我相信我终于找到了一个(如果不是很漂亮的话)解决方案。

据我所知,问题实际上由2个问题组成

  • Windows 控制台代码页默认不正确
  • System.out
    默认使用不正确的编码

从 Java 调整代码页

第一个问题可以使用

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.out 的编码

我在打印时发现这个问题

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
)。


-2
投票

对于阿拉伯语,我使用了以下代码:

PrintWriter stdout = new PrintWriter(
new OutputStreamWriter(System.out,StandardCharsets.ISO_8859_1),true);
© www.soinside.com 2019 - 2024. All rights reserved.