0%

java在windows与vscode的utf8编码乱码问题

windows 使用 UTF-8 编码编写程序(比如 java)会遇到乱码问题。这是因为 windows 本身的默认编码为GBK而不是UTF-8,对应的 windows 下控制台cmd,powershell 的默认显示编码也不是 UTF-8。而系统中安装的编译器本身无从得知一个源代码文件究竟是如何编码的,因而默认采用了系统字符编码,从而导致了问题的发生

永久链接:http://blog.ryjer.com/posts/a11ba50a6.html

原因

java 编译器和java虚拟机并不能知道 .java 源代码文件的字符编码,因为现有的文件系统不会提供该信息。所以 java 需要去 “猜” 源代码文件究竟是什么编码(其他的编译器也需要猜)。

而对于像源代码文件一类的“文本文件”,其编码最有可能采用的就是系统默认的字符编码。而各个操作系统(windows、linux)会在环境变量中向系统中的所有进程提供系统默认字符编码信息。windows 中文版系统默认编码为 GBK,当然(微软传统),是微软自己理解的GBK,并不完全是国标GBK。

所以java会从环境变量中读取当前操作系统的字符编码信息,并将其作为源代码等 java 相关文件的字符编码。

java编译器内部使用的是 Unicode 编码,其只能处理unicode 编码的源代码文件。所以在编译前会先将源代码文件转换为内部的 Unicode编码,然后再进行编译。


当你在windows 下使用 默认字符集为UTF-8 文本编辑器编写 java 源代码时,产生的 .java 源代码文件是与系统默认编码GBK不同的UTF-8 编码文件。而 javac 编译器无法知道这个文本文件到底是什么字符编码,只能将其视作最有可能的系统默认字符编码——GBK编码处理。由于GBK和UTF-8之间的一些差异,在进行 java 外部源代码GBK编码向 java编译器内部Unicode 编码转换时出现了无法转换的问题。便出现了 错误:编码GBK的不可映射字符问题。比如下面这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ javac AllTheColorOFTheRainBow.java 
AllTheColorOFTheRainBow.java:2: 错误: 编码 GBK 的不可映射字符 (0x82)
* 缁冧範11 灏咥llTheColorOfTheRainBow (P36)杩欎釜绀轰緥鏀瑰啓鎴愪竴涓▼搴忥紝鐒跺悗缂栬瘧杩愯銆?
^
AllTheColorOFTheRainBow.java:12: 错误: 编码 GBK 的不可映射字符 (0x9A)
System.out.println("鍒濆闆ㄤ紴棰滆壊鎬绘暟锛?" + rainBow.anIntegerRepresentingColors);
^
AllTheColorOFTheRainBow.java:14: 错误: 编码 GBK 的不可映射字符 (0xB9)
System.out.println("鏀?3鍚庨鑹叉?绘暟锛?" + rainBow.anIntegerRepresentingColors);
^
AllTheColorOFTheRainBow.java:14: 错误: 编码 GBK 的不可映射字符 (0x80)
System.out.println("鏀?3鍚庨鑹叉?绘暟锛?" + rainBow.anIntegerRepresentingColors);
^
AllTheColorOFTheRainBow.java:14: 错误: 编码 GBK 的不可映射字符 (0x9A)
System.out.println("鏀?3鍚庨鑹叉?绘暟锛?" + rainBow.anIntegerRepresentingColors);
^
AllTheColorOFTheRainBow.java:16: 错误: 编码 GBK 的不可映射字符 (0xB9)
System.out.println("鏀?8鍚庨鑹叉?绘暟锛?" + rainBow.anIntegerRepresentingColors);
^
AllTheColorOFTheRainBow.java:16: 错误: 编码 GBK 的不可映射字符 (0x80)
System.out.println("鏀?8鍚庨鑹叉?绘暟锛?" + rainBow.anIntegerRepresentingColors);
^
AllTheColorOFTheRainBow.java:16: 错误: 编码 GBK 的不可映射字符 (0x9A)
System.out.println("鏀?8鍚庨鑹叉?绘暟锛?" + rainBow.anIntegerRepresentingColors);
^
8 个错误

解决方案

知道了javac 编译命令和Java -jar等命令出现字符编码错误的原因,就可以进行针对性解决。主要有以下3种方法,但长期建议使用方法3,短期使用建议采用方法2

1. 修改系统默认字符集编码

该方法建议在 linux 系统上使用,而不建议在 windows 上使用。因为 windows 对 UTF-8 的支持还是不稳定的 beta 版本。

如果你一定要这么做的话,可以按照如下步骤设置。首先,打开 控制面板 > 时钟与区域

image-20210731121852894

然后点击 区域

image-20210731121731680

接着,点击上方 管理

image-20210731122001957

更改系统区域设置

image-20210731122105245

勾选下图红框所示的选项后,点击 确定 完成系统字符集更改

image-20210731122217330

再强调一下,不建议这么做。而是建议使用下面介绍的方法3添加 JAVA_TOOL_OPTIONS 环境变量

2. 手动指定源代码文件的编码字符集

javac 等命令支持手动指定目标文件的编码,这样它们就不用根据系统默认字符集去猜测编码了。

你可以使用 -encoding UTF-8 参数指定文件内的字符编码为 UTF-8,从而显式指明源代码文件的字符集编码。比如,在使用javac 编译命令时可以像如下这样

1
javac -encoding=UTF-8 AllTheColorOFTheRainBow.java

这次再来编译 AllTheColorOFTheRainBow.java 就不会出现中文乱码问题了。

3.添加指定命令参数的环境变量 JAVA_TOOL_OPTIONS

可以通过 JAVA_TOOL_OPTIONS 为系统中java工具包提供额外信息。与之对应,-Dfile.encoding 参数就可以指定java的默认字符集。使得java 工具不再根据系统默认编码处理源代码文件,而是用通过 -Dfile.encoding 指定的编码来处理源代码。你可以通过如下方式指定 java 工具的默认字符集为 UTF-8

在 windows 环境变量中添加一个系统变量 JAVA_TOOL_OPTIONS,其值为 -Dfile.encoding=UTF-8。如果你不知道如何添加环境变量,可以参考这篇文章:如何编辑 windows 10 环境变量

JAVA_TOOL_OPTIONS环境变量

然后点击 确定,回到主编辑窗口,你会发现下图红框所示刚刚被添加的环境变量 JAVA_TOOL_OPTIONS

image-20210731104352638

点击上图所示的 确定 ,回到 系统属性

image-20210731104707303

再点击上图中的确定,完成添加

测试

我这里使用 git bash,其实你用 cmd 、powershell 或者 windows Terminal 都可以。这里使用 javac 编译命令进行测试,其结果如下图所示

1
2
$ javac AllTheColorOFTheRainBow.java
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8

image-20210731105301972

可以看到,javac 命令自动获取了 JAVA_TOOL_OPTIONS 环境变量信息:Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8

依然乱码

如果你上面配置完成后依然乱码,那可能不是或者不仅仅是 java的问题。也可能是你所使用的 shell(或者说是终端) 本身的编码问题,你需要去修改你所使用的终端的默认字符集。

修改 cmd 显示编码

windows下最常见的终端就是cmd,其默认编码一般与系统相同,在中文下是 GBK而不是网络上较为常用的 UTF-8。

你可以使用 chcp 命令修改 cmd 终端的默认字符集,其格式为

1
chcp 编码代号

不同的代号代表不同的编码,常用的代号如下

1
2
3
# 编码格式        chcp 代号
UTF-8 65001
GBK 937

所以,要更改显示字符集为 UTF-8的命令是

1
chcp 65001

你在 cmd 中执行以上命令即可更改 cmd 的默认字符集为 UTF-8