Yas*_*jaj 5 java encoding compilation utf-8 character-encoding
我有以下代码
public class MainDefault {
public static void main (String[] args) {
System.out.println("²³");
System.out.println(Arrays.toString("²³".getBytes()));
}
}
Run Code Online (Sandbox Code Playgroud)
但似乎无法将特殊字符打印到控制台
当我执行以下操作时,我得到以下结果
$ javac MainDefault.java
$ java MainDefault
Run Code Online (Sandbox Code Playgroud)
另一方面,当我编译它并像这样运行它时
$ javac -encoding UTF8 MainDefault.java
$ java MainDefault
Run Code Online (Sandbox Code Playgroud)
当我使用文件编码 UTF8 标志运行它时,我得到以下信息
$ java -Dfile.encoding=UTF8 MainDefault
Run Code Online (Sandbox Code Playgroud)
控制台(Windows 10 上的 Git Bash)似乎没有问题,因为它可以正常打印字符
谢谢你的帮助
您的代码没有在控制台中打印正确的字符,因为您的 Java 程序和控制台使用不同的字符集、不同的编码。
如果要获得相同的字符,首先需要确定哪些字符集到位。
此过程将取决于您输出结果的“控制台”。
如果您使用的是 Windows 和cmd,正如@RickJames 建议的那样,您可以使用该chcp命令来确定活动代码页。
Oracle提供了Java全面支持的编码信息,并与其他别名的对应-在这种情况下,代码页-在这个页面。
此stackoverflow 答案还提供了一些有关 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=
Run Code Online (Sandbox Code Playgroud)
知道这些信息后,您需要使用与file.encoding正确字符集对应的VM 选项运行 Java 程序:
java -Dfile.encoding=UTF8 MainDefault
Run Code Online (Sandbox Code Playgroud)
某些类,如PrintStream或PrintWriter,允许您指示Charset将输出信息的位置。
该-encoding javac选项仅允许您指定源文件使用的字符编码。
如果您将 Windows 与 Git Bash 结合使用,请考虑阅读此@rmunge答案:它提供了有关工具中可能存在的错误的信息,该错误可能是问题的原因,并且会阻止终端在不需要的情况下开箱即用用于手动编码调整。
我也在Windows 10 上使用Git Bash,它对我来说完全正常。
这是它的打印方式,
终端版本是mintty 3.0.2 (x86_64-pc-msys),我的文本属性是,
因此,我尝试通过更改字符集来重现您的输出;
通过将 Character Set 设置为CP437 (OEM codepage)(请注意,这C也会自动将 Locale 更改为),我可以获得您所获得的输出。
然后当我将它改回 后UTF-8 (Unicode),我可以按预期获得输出!
因此,很明显问题出在控制台的字符集上。
简短版本:
\n通过以下设置可以重现意外行为:
\n使用英语、德语或法语的 Windows 10,或导致以不同方式编码 \xc2\xb2 和 \xc2\xb3 的 ANSI 和 OEM 代码页的任何其他语言
\nGit for Windows 2.27.0(使用默认设置安装,即\n配置为使用 MinTTY 和对伪控制台的实验性支持\n已禁用)
\n源代码以UTF-8编码存储
\n要获得正确的行为:
\n重新安装 Git for Windows 2.27.0 并在安装程序的最后一页启用对伪控制台的实验性\n支持,或\n升级到最新的 2.28 版本
\n使用 javac -encoding UTF8 编译代码
\n调用java而不覆盖file.encoding
\n中号版本:
\nGit for Windows 2.27.0 使用MSYS2版本,当禁用对伪控制台的支持时,该版本不会通过调用SetConsoleCP来设置 MinTTY 的代码页。Java 运行时System.out通过调用GetConsoleCP确定代码页。由于在 MinTTY 终端内执行 Java 时未设置代码页,因此调用失败并且 Java 使用 返回的字符集作为Charset.defaultCharset()后备。但在如上所述的 Windows 安装中,Charset.defaultCharset()返回Cp-1252而控制台的默认字符集是Cp-850。这两个代码页不完全兼容。这导致了奇怪的输出。
长版本:
\nWindows 有两种类型的代码页:ANSI 和 OEM 代码页。第一种类型适用于不支持 Unicode 的 UI 应用程序,后者用于控制台应用程序。这两种类型都以 1 字节编码单个字符,但它们并不完全兼容。
\n因此,在 Windows 上,Java 必须处理两种字符集,而不是一种:
\nCharset.defaultCharset()返回 ANSI 代码页(通常为 cp-1252)。该字符集由file.encoding系统属性指定。如果未指定为 VM 参数,则 java 可执行文件将确定 ANSI 代码页并在初始化期间添加系统属性。String.getBytes()使用返回的字符集Charset.defaultCharset()。System.out使用控制台的 OEM 代码页(通常是 cp-850)。java 可执行文件通过调用GetConsoleCP函数获取此代码页,并将其设置为内部系统属性sun.stdout.encoding和sun.stdout.encoding的值。当对GetConsoleCP 的调用失败时,将使用返回的字符集Charset.defaultCharset()。仅当执行 java.exe 的控制台之前未通过调用SetConsoleCP设置 OEM 代码页时才会发生这种情况那么在上述设置中现在会发生什么?
\n$ javac MainDefault.java\n$ java MainDefault\nRun Code Online (Sandbox Code Playgroud)\n\n由于MSYS2 中的错误,GetConsoleCP的本机调用失败。因此回退到返回的字符集cp-1252。但控制台的 OEM 代码页是 cp-850。因此 System.out.println("\xc2\xb2\xc2\xb3") 会产生意外的输出。System.outCharset.defaultCharset()
源代码以UTF-8存储。以 UTF-8 编码“\xc2\xb2\xc2\xb3”需要 4 个字节。但由于缺少-encoding参数,javac 假定每个字符使用一个字节的默认编码。因此它将 4 个字节解释为 4 个字符。String.getBytes使用 1 字节、基于 ANSI 代码页 cp-1252,因此返回 4 个字节。
$ javac -encoding UTF8 MainDefault.java\n$ java MainDefault\nRun Code Online (Sandbox Code Playgroud)\n\n使用-encoding UTF8参数 javac 将 UTF-8 编码的源解释为 UTF-8。因此“\xc2\xb2\xc2\xb3”的4个字节被正确识别为两个字符。System.out对 cp-1252 中的两个字符进行编码,得到 2 个字节。但由于控制台仍然使用 cp-850,输出仍然损坏。String.getBytes也在 cp-1252 中对 wo 字符进行编码,这导致 2 个字节。
$ java -Dfile.encoding=UTF8 MainDefault\nRun Code Online (Sandbox Code Playgroud)\n\n系统属性file.encoding会覆盖 所返回的字符集,Charset.defaultCharset()该字符集也由String.getBytes(). 最初被 javac 错误地解释为 8 位编码中的 4 个字符的两个字符现在在 UTF-8 中正确编码为每个字符编码为两个字节的两个字符。这导致 4 个字节。由于file.encoding对 4 个(而不是 2 个,由于 javac 的错误解释)字符所使用的字符集没有任何影响,System.out字符仍然以 cp-1252 进行编码,因此控制台仍然使用 cp-850 并且您仍然会得到结果损坏的输出。
您的控制台可以打印 \xc2\xb2\xc2\xb3,因为控制台的 8 位 OEM 代码页 (cp-850) 支持这两个字符。System.out但它的编码方式与;-)使用的 ANSI 代码页 cp-1252 略有不同