【问题标题】:UTF-8 does not print characters to the consoleUTF-8 不会将字符打印到控制台
【发布时间】:2020-09-02 19:05:36
【问题描述】:

我有以下代码

public class MainDefault {
        public static void main (String[] args) {
                System.out.println("²³");
                System.out.println(Arrays.toString("²³".getBytes()));
        }
}

但似乎无法将特殊字符打印到控制台

当我执行以下操作时,我得到以下结果

$ javac MainDefault.java
$ java MainDefault

另一方面,当我像这样编译并运行它时

$ javac -encoding UTF8 MainDefault.java
$ java MainDefault

当我使用文件编码 UTF8 标志运行它时,我得到以下信息

$ java -Dfile.encoding=UTF8 MainDefault

控制台(Windows 10 上的 Git Bash)似乎没有问题,因为它可以正常打印字符

感谢您的帮助

【问题讨论】:

  • 可能是 thisthis one(我在 IntelliJ 上试过,看到了正确的输出)
  • 组成字符串 -62,-78,-62,-77 的数字序列是(作为无符号字节)0xC2,0xB2,0xC2,0xB3。这些是屏幕截图中出现的 ASCII 框字符的 CP437 值。这些值也可能出现在其他字符集中,但不会出现在 UTF-8 甚至 ISO-88591-1 中。看起来好像正在编译的文件不是 UTF-8,或者显示输出的终端未设置为显示 UTF-8。如果问题出在文件的编码中,那么 System.out.println("\u00B2\u00B3") 应该会产生正确的输出,因为这些是 ²³ 的 Unicode 转义
  • 我在 Mac 和 Git Bash for Mac 上得到了预期的输出。可能是 Windows 的问题。

标签: java encoding utf-8 compilation character-encoding


【解决方案1】:

您的代码没有在控制台中打印正确的字符,因为您的 Java 程序和控制台使用不同的字符集、不同的编码。

如果要获取相同的字符,首先需要确定有哪些字符集。

此过程将取决于您在其中输出结果的“控制台”。

如果您使用 Windows 和 cmd,正如 @RickJames 建议的那样,您可以使用 chcp 命令来确定活动代码页。

Oracle 在this 页面中提供了Java 完全支持的编码信息,以及与其他别名(本例中为代码页)的对应关系。

Thisstackoverflow 的回答还提供了一些关于 Windows 代码页和 Java 字符集之间映射的指导。

正如您在提供的链接中看到的,UTF-8 的代码页是 65001

如果您使用的是 Git Bash (MinTTY),您可以按照 @kriegaex 的说明验证或配置 UTF-8 为终端仿真器编码。

Linux 和 UNIX 或 UNIX 派生系统(如 Mac OS)不使用代码页标识符,而是使用语言环境。区域信息可能因系统而异,但您可以使用locale 命令或尝试检查LC_* 系统变量以查找所需信息。

这是我系统中locale 命令的输出:

LANG="es_ES.UTF-8"
LC_COLLATE="es_ES.UTF-8"
LC_CTYPE="es_ES.UTF-8"
LC_MESSAGES="es_ES.UTF-8"
LC_MONETARY="es_ES.UTF-8"
LC_NUMERIC="es_ES.UTF-8"
LC_TIME="es_ES.UTF-8"
LC_ALL=

一旦您知道了这些信息,您需要使用与正确字符集对应的file.encoding VM 选项来运行您的 Java 程序:

java -Dfile.encoding=UTF8 MainDefault

某些类,如PrintStreamPrintWriter,允许您指明将在其中输出信息的Charset

-encodingjavac 选项仅允许您指定源文件使用的字符编码。

如果您使用带有 Git Bash 的 Windows,请考虑阅读此 @rmunge answer:它提供了有关工具中可能存在的错误的信息,该错误可能是问题的原因,并且会阻止终端正常运行该框无需手动编码调整。

【讨论】:

  • 嗨,JCCampanero,这帮助我找到了正确的答案,所以我认为它是有效的。我将字符打印到控制台的方法是使用chcp.com 65001,然后再次运行我的脚本,它起作用了:D
  • 非常感谢@YassinHajaj!我很高兴知道这个答案很有帮助。
  • @YassinHajaj 请注意 chcp 仅更改正在运行的控制台的 OEM 代码页。再次关闭并打开控制台后,代码页将恢复为默认值。
  • 这个答案没有解释所描述行为的原因。它只提供了一些不需要的解决方法的一般提示。
  • @rmunge 请不要担心。事实上,您的答案有据可查,并提供了很好的背景信息。我赞成它并参考它更新了我的答案。最后,唯一重要的是答案尽可能对用户有用。
【解决方案2】:

我也在 Windows 10 上使用 Git Bash,它对我来说完全没问题。

这是它的打印方式,

终端版本是mintty 3.0.2 (x86_64-pc-msys),我的文本属性是,

所以,我尝试通过更改字符集来重现您的输出;

通过将字符集设置为CP437 (OEM codepage)(请注意,这也会自动将语言环境更改为C),我可以得到你得到的输出。

然后当我把它改回UTF-8 (Unicode) 之后,我可以得到预期的输出!

因此,很明显问题出在控制台的字符集上。

【讨论】:

  • 这基本上是我前几天自己的答案的重复,甚至截图都是一样的。这个更冗长。
  • @kriegaex 我想重现并解决问题,这样我们就可以清楚地了解问题出在哪里。所以,我发布了我所做的。
【解决方案3】:

对于 UTF-8,十六进制代码看起来不错。也许你的 Git Bash 字符集不是 UTF-8。对我来说,它看起来像这样:

然后控制台输出看起来也很好:


2020 年 9 月 13 日更新: 这是 chcp.com <codepage> 在 Git Bash(薄荷)中工作的证据。它没有任何效果。您确实必须在 mintty 设置对话框中选择正确的代码页。


2020-09-15 更新: 好的,在我阅读了 @rmunge 的回答后,我升级到了 Git 2.28,可以重现 OP 的问题,还可以使用 chcp 解决方法(它不能作为在我的情况下由@rmunge 描述)。因为 Git(或分别为 MSYS2)在最新版本中有很多 bug,我不想每次打开新控制台时都在 Git Bash 中使用chcp.com,所以我只是降级到我使用过的 2.15.1 版本3年以前没有任何问题。也许有没有控制台错误的更高版本,我没有尝试,只是使用计算机上下载文件夹中的旧安装程序。我建议每个人都这样做,现在解决这个丑陋的错误。使用没有错误的控制台版本,它就像我描述的那样工作。

【讨论】:

  • 非常感谢您抽出宝贵时间,但不幸的是,这并没有解决问题
  • 你不公平!您接受的解决方案是针对 CMD,但您发布的屏幕截图来自带有 mintty 终端的 Git Bash(您也在文中提到),我可以看到它,因为命令提示符来自类似 Bash 的外壳。 chcp 命令对 mintty 和 Git Bash 完全没用。对于您的问题,我的答案是正确的,接受的答案仅适用于 CMD。你怎么能接受一个问题并奖励 500 分回答你没有问的问题?
  • 嗨@kriegarx ... chcp.com 在git bash 上工作,我不太明白你的意思.. 它有效,我将赏金奖励给最接近现实的答案,他的回答是一..不需要完全情绪化,那些是虚拟点..
  • 不,它没有。我刚刚重新测试了它。 chcp.com 65001 对 mintty Git Bash 没有影响。即使随后的chcp.com 显示“65001”,输出也是错误的。只有在 mintty 控制台设置中选择 UTF-8 时,结果才看起来正确。如果你喜欢,我可以发布一个屏幕视频来证明它。如果您另有主张,请发布屏幕视频来证明您所说的话。这不是真的!
  • 更新: 我刚刚在我的答案中添加了一个屏幕录制(动画 GIF)。证明我的回答是正确的。就像我说的,chcp.com 仅适用于 CMD 控制台。即使是回答问题的人也证实了这一点,并指出了我对 Git Bash 的回答。你感谢他chcp 65001,尽管你从来没有问过 CMD,而是明确地问过 Git Bash。
【解决方案4】:

简短版:

使用以下设置可以重现意外行为:

  • Windows 10 使用英语、德语或法语,或任何其他导致 ANSI 和 OEM 代码页对 ² 和 ³ 进行不同编码的语言

  • Git for Windows 2.27.0(安装默认设置,即 配置为使用 MinTTY 和对伪控制台的实验性支持 禁用)

  • 源代码以UTF-8编码存储

要获得正确的行为:

  • 要么重新安装 Git for Windows 2.27.0 并启用实验性 在安装程序的最后一页支持伪控制台或 升级到最新的 2.28 版本

  • 使用 javac -encoding UTF8 编译代码

  • 在不覆盖 file.encoding 的情况下调用 java

中等版本:

Windows 2.27.0 版 Git 使用 MSYS2 版本,当禁用对伪控制台的支持时,该版本不会通过调用 SetConsoleCP 为 MinTTY 设置代码页。 Java 运行时通过调用GetConsoleCP 来确定@​​987654339@ 的代码页。由于在 MinTTY 终端中执行 Java 时没有设置代码页,因此调用失败并且 Java 使用 Charset.defaultCharset() 返回的字符集作为后备。但在如上所述的 Windows 安装中,Charset.defaultCharset() 返回Cp-1252,而控制台的默认字符集为Cp-850。这两个代码页不完全兼容。这会导致奇怪的输出。

长版:

Windows 有两种类型的代码页:ANSI 和 OEM 代码页。第一种用于不支持 Unicode 的 UI 应用程序,后者用于控制台应用程序。两种类型都以 1 字节编码单个字符,但它们并不完全兼容。

因此,在 Windows 上,Java 必须处理两个字符集而不是一个:

  • Charset.defaultCharset() 返回 ANSI 代码页(通常为 cp-1252)。此字符集由 file.encoding 系统属性指定。如果未指定为 VM 参数,则 java 可执行文件确定 ANSI 代码页并在初始化期间添加系统属性。 String.getBytes() 使用 Charset.defaultCharset() 返回的字符集。
  • System.out 使用控制台的 OEM 代码页(通常是 cp-850)。 java 可执行文件通过调用GetConsoleCP 函数获取此代码页,并将其设置为内部系统属性sun.stdout.encodingsun.stdout.encoding 的值.当对GetConsoleCP 的调用失败时,将使用Charset.defaultCharset() 返回的字符集。这只发生在执行 java.exe 的控制台之前没有设置 OEM 代码页时,通过调用 SetConsoleCP

那么现在在上面提到的设置中会发生什么?

$ javac MainDefault.java
$ java MainDefault

GetConsoleCP 的本机调用因bug in MSYS2 而失败。因此System.out 回退到Charset.defaultCharset() 返回的字符集,即cp-1252。但是控制台的 OEM 代码页是 cp-850。因此 System.out.println("²³") 会产生意外的输出。

源代码以 UTF-8 格式存储。以 UTF-8 编码“²³”需要 4 个字节。但是由于缺少 -encoding 参数,javac 假定默认编码每个字符使用一个字节。因此它将 4 个字节解释为 4 个字符。 String.getBytes 使用 1 字节,基于 ANSI 代码页,cp-1252,因此返回 4 字节。

$ javac -encoding UTF8 MainDefault.java
$ java MainDefault

使用 -encoding UTF8 参数 javac 将 UTF-8 编码的源解释为 UTF-8。因此,“²³”的 4 个字节被正确识别为两个字符。 System.out 将 cp-1252 中的两个字符编码为 2 个字节。但是由于控制台仍然使用 cp-850 输出仍然损坏。 String.getBytes 也对 cp-1252 中的 wo 字符进行编码,导致 2 个字节。

$ java -Dfile.encoding=UTF8 MainDefault

系统属性file.encoding 覆盖Charset.defaultCharset() 返回的字符集,String.getBytes() 也使用该字符集。最初被 javac 错误解释为 8 位编码中的 4 个字符的两个字符现在在 UTF-8 中被正确编码为每个字符两个字节编码的两个字符。这导致 4 个字节。由于 file.encodingSystem.out 使用的字符集没有任何影响,因此 4 个(而不是 2 个,由于对 javac 的错误解释)字符仍然在 cp-1252 中编码,控制台仍然使用 cp-850,你仍然得到一个损坏的输出。

您的控制台可以打印 ²³,因为控制台的 8 位 OEM 代码页 (cp-850) 支持这两个字符。但它对它的编码与System.out 使用的 ANSI 代码页 cp-1252 略有不同;-)

【讨论】:

  • 你好@rmunge,谢谢你的扩展回答,但我发誓使用 chcp.com 65001 为我修复了它,添加它可能会很有趣
  • @YassinHajaj 使用 chcp.com 65001 将 OEM 代码页更改为 UTF-8,-Dfile.encoding 将 Charset.defaultCharset() 返回的字符集也更改为 UTF-8。由于 Git Bash 中的错误,System.out 的字符集也回退到 UTF-8。由于控制台现在也使用 UTF-8,所以一切正常,这是一个有效的解决方法。该解决方法也适用于不包含该错误的较新 Git Bash 版本,但请注意,在执行 java 之前,您必须在每个新控制台上执行 chcp.com ;-)
  • @rmunge,对我来说,升级到 2.28 后的默认代码页是 US-ASCII(ID 20127,也由 chcp.com 确认)。实验控制台支持已启用。所以对我来说,你的解决方法不像描述的那样工作,我需要在设置中组合 UTF-8 mintyy,在 Git Bash 中 chcp.com 850(多么奇怪!)和可选的 -Dfile.encoding=UTF8(实际上没有效果,也超级奇怪)。顺便说一句,我的 Windows 10 是德语。现在我很遗憾在对您的解决方案感到好奇后升级了我的旧 Git 版本。以前,我自己的解决方案工作得很好,但现在不行了。 Git/MSYS2 现在有问题了!
  • chcp.com 65001 也可以,但如果没有 chcp,Java 输出总是错误的。以防万一您参与了 MSYS2 或 Windows 版 Git,您知道这是否正在积极工作并且可能很快就会修复?否则我可能会再次降级我的 Git。
  • 好的,我刚刚降级到 Git 2.15.1.windows.2(我的下载文件夹中仍然有 2017 年的旧安装程序),一切都很好,我自己的解决方案有效,但我没有注意强制降级后的任何直接问题。
【解决方案5】:

在 Windows 上,它与您的代码页有关。 您可以使用命令 chcp 来设置您想要的代码页(例如:如果您想为启动的特定程序设置它)或者您可以在 java 命令行中指定与代码页对应的字符集。

如果当前代码页不支持您正在打印的字符,您将在控制台中看到垃圾。

不同的shell可能表现不同的原因是默认加载的代码页/字符集。

请查看此 SO 帖子以了解它是如何完成的: System.out character encoding

【讨论】:

  • 确实需要更改终端的代码页,谢谢!
【解决方案6】:

十六进制 C2B2 C2B3,当解释为 UTF-8 时为 ²³

我假设您使用的是 Windows“cmd 终端”?

命令“chcp”控制“代码页”。 chcp 65001 提供 utf8,但它也需要安装一个特殊的字符集。要在控制台窗口中设置字体:右键单击窗口标题 → 属性 → 字体 → 选择 Lucida Console

【讨论】:

  • 截图和 OP 自己的话都告诉你他使用的是 Git Bash,而不是 cmd.exe。 ;-)
  • Bash 是一种脚本语言; cmd 是渲染应用程序。它们是不同的动物。 (两者都需要。)
  • 你想在这里吹毛求疵?然后我也会:Cmd 和 Bash 都是 shell(命令处理器)。您可以从 Cmd 启动 Bash,反之亦然。如果您通过单击图标在 Windows 上启动 Git Bash,则会自动启动单独进程中的薄荷终端仿真器窗口,就像单击 Windows 终端图标时它也会启动终端的 conhost.exe 一样。如果您打开子 shell,则不会启动其他终端进程。另请参阅我在此处使用薄荷设置屏幕截图的答案。当然,从 Windows 终端启动 bash.exe 是可能的,但很少见。
【解决方案7】:

请确认您的 Windows 10 安装没有启用了 Unicode UTF-8 支持。您可以通过转到设置来查看此选项,然后:所有设置 -> 时间和语言 -> 语言 -> “管理语言设置”

这就是它的样子 - 应该取消选中该功能。

理由:

"²³".getBytes() 根据检测到的默认字符集返回字符串的编码。在 Windows 10 系统上,默认字符集通常应该是基于 1 字节的编码,与您是从 Windows 控制台还是从 Git Bash 启动 java.exe 无关。但是您的第一个屏幕截图显示了一个实际上是 UTF-8 的 4 字节编码。因此,您的 JVM 似乎将 UTF-8 检测为与控制台代码页不兼容的错误默认字符集。

您的控制台可以打印 ²³,因为使用的代码页支持这两个字符,但编码基于每个字符一个字节,而 UTF-8 编码要求这两个字符中的每一个都需要 2 个字节。

我对您的第二个屏幕截图没有简单的解释,但请注意 Git Bash 基于MSYS2,它再次使用mintty 终端仿真器。虽然 MSYS2 使用 UTF-8,并且 mintty 似乎也支持 UTF-8,但整个事情都包装在一个 Windows 控制台中,该控制台基于与 UTF-8 不兼容的 OEM 代码页。然后整个事情在内部使用 UTF-16 的操作系统上运行。现在,结合在操作系统级别推翻整个 OEM 代码库概念的 beta 设置,此设置为一些难以理解的行为提供了足够的复杂性。

【讨论】:

  • 非常感谢您抽出宝贵时间,但不幸的是,这并没有解决问题
  • 太糟糕了。 :-( 请使用 System.out.println(Charset.defaultCharset().name()); 扩展您的代码,并在您在 Git Bash 和 cmd.exe 中执行时共享输出(无需指定任何额外的虚拟机参数)。如果我对错误默认字符集的第一个假设是正确的,以及 Git Bash 和 cmd.exe 之间是否存在差异,将会很有趣。
  • @YassinHajaj 你是如何开始你的 Git Bash 的?您是单击“Git Bash”快捷方式还是直接执行 git-bash.exe 甚至 bash.exe?你用的是什么java版本?
  • 嘿@rmunge,为了给你更多的背景信息,弹出窗口包含法语并且该框未被选中。我通过在资源管理器中右键单击并单击“打开 Git Bash”来打开 git bash,或者有时我单击 Windows 开始(左下角)并输入“Git..”然后输入,因为 Git bash 先出现
  • 关于 Sysout,我一定会尽快为您提供此信息,因为它在另一台 PC 上,不在 ATM 上,一旦我打开它,我会通知您
【解决方案8】:

我在 Windows 的 git bash 中遇到了同样的问题。 javajavac 无法正常打印汉字。将 git-bash 的字符集设置为 UTF8 并没有帮助。 chcp 也不起作用。从 git bash 的安装向导中,我知道像 python 这样的程序在没有 winpty 的情况下无法正常工作。我已将alias python='winpty python 添加到~/.bashrc。所以我尝试了winpty java Foo.javawinpty javac Foo.java,幸运的是问题消失了。我将别名添加到 ~/.bashrc 以解决问题:

alias java='winpty java'
alias javac='wintpy javac'

Windows 版 git bash 的最新版本 (v2.2x) 包含一个关于 winpty 的实验性功能,但它似乎仍然存在一些问题,所以我一直保留这些别名。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-20
    • 2018-06-18
    • 1970-01-01
    • 2011-08-06
    相关资源
    最近更新 更多