java默认字符编码问题及解决
在宝蓝德适配中,主机授权信息出现乱码。分析发现JNI返回的UTF-8字节数组被newString()使用默认字符集ANSI_X3.4-1968转换,原因是启动脚本中LC_ALL被设为POSIX。可通过设置locale或添加-Dfile.encoding=UTF-8解决。应显式指定字符集,注意父进程环境变量影响子进程locale。
问题现象
在宝蓝德适配过程中遇到一个有点恼人的问题:主机授权信息里的客户名称和部门名称,展示出来全是乱码。具体现象长这样:

分析思路
分析乱码的原因
先看看获取这个参数值的方法——核心代码其实很简洁:
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工具分析一下这组字节——按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)这一步——它用的是Ja va进程的默认字符集,而这个默认字符集并非UTF-8。
分析默认编码为何非预期UTF-8
接下来必须搞清楚:默认Charset到底是怎么来的。翻一下Charset.defaultCharset()的源码,核心逻辑就是读取系统属性file.encoding:
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源码显示,JVM启动时会从本地locale中获取,具体是读取LC_CTYPE里的encoding信息。比如如果LC_CTYPE是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命令,当前环境输出显示的是utf-8类型:

可是实际Ja va进程的file.encoding却是ANSI_X3.4-1968。对比下来,只能得出一个结论:启动Ja va进程的那个上下文里,locale被单独修改了。在启动脚本里加一行输出locale的日志,果然——LC_ALL被设成了POSIX,而不是期望的xxx.utf-8。
解决方案
- 方案一:在
bes.sh启动BES程序之前,把locale设置为UTF-8编码即可。 - 方案二:既然
file.encoding是从系统属性读的,直接加JVM参数-Dfile.encoding=UTF-8,强制指定。
总结
- 为了程序的可移植性,在
new String、IO流等场景中,如果已知字符集,务必显式指定字符集参数,别依赖默认值。 - 程序设计中要特别留意:父进程设置的环境变量或参数,会直接影响子进程的locale和行为,这个坑很容易踩到。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















