java默认字符编码问题及解决
作者:las_learn
问题现象
在进行宝蓝德适配时发现主机的授权信息中客户名称和部门名称展示为乱码, 针对该问题进行分析,
现象如下:

分析思路
分析乱码的原因
分析获取的该参数的值方法:
public String getBin(Pointer lic, String column) {
Pointer buf = new Memory(256);
IntByReference bufsize = new IntByReference();
bufsize.setValue(256);
if (INSTANCE.license_get_bin(lic, column, buf, bufsize) < 0) {
return "";
}
byte[] value = buf.getByteArray(0, bufsize.getValue());
return new String(value);
}可以看出就是通过jni函数获取对应字段的byte数组,转换为字符串,通过远程debug连接后,发现获取的字符串,发现确实是乱码, 进一步分析从jni中获取的byte数组列表,得到的数据为[-28, -72, -83, -24, -81, -107, -25, -84, -74, -23, -98, -81, -26, -104, -112], 通过ai分析得到如下结果:
这组 byte 按 UTF-8 解码对应的字符串是:
测试服务器:
可用下面代码验证:
byte[] b = new byte[] {-28, -72, -83, -24, -81, -107, -25, -84, -74, -23, -98, -81, -26, -104, -112
};
System.out.println(new String(b, StandardCharsets.UTF_8));说明byte数组转换为字符串确实为预期的值,只能说明是new String(value);时使用到java进程的默认字符集导致的即对应的字符集非UTF-8编码
分析默认编码为何非预期UTF-8
需要分析默认的charset的获取方式,阅读Charset的源码中可以看到,是通过读取file.encoding的property获取。
public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
String csn = AccessController.doPrivileged(
new GetPropertyAction("file.encoding"));
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}通过arthas工具,查看问题节点的file.encoding非UTF-8编码,而为ANSI_X3.4-1968类型。

接下来分析file.encoding是如何设置的,通过分析jdk和从AI结合分析,得到在jvm启动时会尝试从本地的locale中获取,会从LC_CTYPE中获取encoding信息,实际实现为en_US.UTF-8 ,设置encoding就是UTF-8 部分(有兴趣的可以设置LC_CTYPE参数,查询file.encoding值)。
具体jdk代码如下:
setlocale(LC_ALL, "");
if (ParseLocale(env, LC_CTYPE,
&(sprops.format_language),
&(sprops.format_script),
&(sprops.format_country),
&(sprops.format_variant),
&(sprops.encoding))在bash中执行locale命令, 获取到的locale输出却是utf-8类型

综合现象和默认值对比,只能是启动java进程的进程上下文中配置的locale被单独设置导致的,在进程启动脚本中添加输出locale的日志,发现确实被修改为POSIX了,非xxx.utf-8.
解决方案
- 方案一: 在bes.sh执行启动bes程序前设置locale为UTF-8编码即可。
- 方案二: 已知会从系统properties中获取,可以加-Dfile.encoding=UTF-8参数的方式设置
总结
1、为了保障程序的通用性,如果已知字符集时new String, IO流操作场景,最好都配置上字符集
2、程序设计时注意程序设置环境变量或参数对子进程的影响。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
