properties文件编码问题

发布时间 2023-12-26 16:04:04作者: ZachLim

问题排查

最近编写项目中的 properties 文件时,发现文件中存在如下所示的乱码字符:

foo.properties

# 中文
name=����

由于笔者 IDEA 设置默认使用 UTF-8 打开 properties 文件,所以很自然地想到是文件编码问题。

> file foo.properties
foo.properties: ISO-8859 text

使用 file 命令看到程序猜测的是 ISO-8859 编码,但这编码显然不对,ISO-8859 是8位字符集无法表示中文,
明显不符合笔者所处的场景,基于常识那就大概率是国标编码即GB2312、GBK、GB18030等
(具体区别自行搜索,只需知道GB18030兼容GBK,而GBK又兼容GB2312,而GB2312包含了大部分日常使用汉字)。

为了明确文件的编码,使用 iconv 命令对文件编码进行转换输出,若终端输出不乱码即可确认文件编码。

1、首先用 locale 命令查看当前终端设置字符集编码,此编码用于输出转换使用,若编码不正确会导致输出乱码;

> locale|grep "LANG="
LANG=C.UTF-8

2、接着用 iconv 命令将文件的编码由 gb2312 转换成终端设置的编码 utf8 并输出。

> iconv -f gb2312 -t utf8 foo.propeties
# \u4e2d\u6587
name=中文

可以看到输出正常显示,所以文件的正确编码是 gb2312 。

字符转义

上面输出中可以看到注释那一行,是 \u 开头加上4位十六进制的为一组的字符串。

那么这一串字符是什么呢?为什么和 IDEA 中显示的不一致?

先解答第一个疑问这一串是 unicode 字符,我们可以如下使用 echo 命令或者 JDK 的 native2ascii 工具转义输出:

> echo -e '\u4e2d\u6587'
中文
> echo '\u4e2d\u6587' | native2ascii -reverse -encoding utf8
中文

在解答 IDEA 显示问题前,先思考为什么要进行转义呢?

那就要先回到本文最初的问题乱码上,上文中提到的文件编码都是基于猜测的,因为不知道文件编码所以只能猜测,
猜不中的话就会产生乱码问题。

那么怎么避免乱码问题呢?其实很简单就是读取前知道编码即可。

  • 例如 HTTP 协议里 Content-Type 请求头的 charset 显式地指定了编码;
  • Java 语言里隐式地约定字符使用 Unicode 编码。

而这里的转义其实是隐式地约定 properties 文件使用一种编码,读取时使用该编码读取即可,
遇到转义字符转义成对应 Unicode 字符即可,这样便避免了乱码问题。

其实在 JDK 的 java.util.Properties 类的 load(InputStream inStream) 方法上,
就有下面一段注释说明:

The input stream is in a simple line-oriented format as specified in load(Reader) and is assumed to use the ISO 8859-1 character encoding; that is each byte is one Latin1 character. Characters not in Latin1, and certain special characters, are represented in keys and elements using Unicode escapes as defined in section 3.3 of The Java™ Language Specification.

其大致意思是:假定输入都是使用 ISO 8859-1 字符集编码,不在字符集里字符会使用 Unicode 转义。

自动转换

若是每次修改 properties 文件,都得先反转义回正常文本,修改后又在转义一下会特别的麻烦。

为此 IDEA 有了自动转换功能(Eclipse 里也有此功能),IDEA 会自动转义后存储,读取显示时又会自动反转义,
这也解释了为什么其他编辑器或者命令行程序输出和 IDEA 显示不一致的问题。

小提示

IDEA 设置 “File Encoding” -> “Transparent natvie-to-ascii conversion” 这个选项默认是勾选的,可去掉勾选禁用自动转换。

native2ascii工具

上文中提到了 JDK 里提供了 native2ascii 工具,该工具可以对文件的转义及其反操作,
可用该工具对文件批量操作,以下是该工具的使用方法:

Usage: native2ascii [-reverse] [-encoding encoding] [inputfile [outputfile]]

例如对 foo.properties 文件进行转义,例子如下:

> native2ascii -encoding gb2312 foo.properties
# \u4e2d\u6587
name=\u4e2d\u6587

可以看到其中的“中文”被转义输出了,其他字符还是保持原样,而使用-reverse选项就能反转义,例子如下:

> native2ascii -encoding gb2312 foo.properties | native2ascii -reverse -encoding gb2312 |iconv -f gb2312 -t utf8
# 中文
name=中文

编码选择

笔者对项目里的 properties 文件扫描后,发现存在各种编码,那么究竟如何选择呢?

> find -iname "*.properties" -type f | xargs file

./src/main/resoruce/application.properties:  ISO-8859 text, with CRLF line terminators
./src/main/resoruce/bootstrap.properties:    UTF-8 Unicode text, with CRLF line terminators
./src/main/resoruce/translate.properties:    ASCII text, with very long lines, with CRLF line terminators

显而易见的是应该使用 JDK 里描述的 ISO 8859-1 编码,这样程序读取时就无需指定编码,也不会出现乱码问题。

总结

本文从常见的 properties 文件乱码问题入手,进一步了解字符编码乱码及 properties 文件转义问题。