Charset.forName() 不能加载非标准编码名称,仅支持 JDK 注册的标准别名(如 "GBK"、"GB18030"),非法名称抛 UnsupportedCharsetException;处理中文乱码应优先用 "GB18030",fallback 到 "GBK";自定义编码需通过 CharsetProvider 注册。

Charset.forName() 能否加载非标准编码名称
不能直接加载——Charset.forName() 只认 java.nio.charset.Charset 注册的标准别名(如 "GBK"、"GB18030"、"ISO-8859-1"),不支持任意字符串如 "gb2312"(注意大小写)或拼写错误的 "UTF-8 "(尾部空格)。传入非法名称会抛出 UnsupportedCharsetException 或 IllegalArgumentException。
常见误操作:
- 用系统 locale 默认编码名(如 Windows 上写
"GBK",但 Linux 下可能不存在) - 混淆别名与 IANA 名称(
"Big5"合法,"BIG5-HKSCS"在部分 JDK 版本中不被识别) - 依赖第三方 jar 提供的 charset(如
"HZ-GB-2312"),但未注册到 JVM
如何安全地 fallback 到 GBK/GB18030 处理中文乱码文件
很多老旧文本文件标称 "GB2312",但实际含扩展字,必须用 "GB18030" 才能完整解码。硬写 Charset.forName("GB2312") 会漏字甚至抛异常。
推荐做法是显式降级尝试:
立即学习“Java免费学习笔记(深入)”;
- 优先用
"GB18030"(兼容 GBK 和 GB2312,JDK 1.6+ 原生支持) - 若明确需 GBK 且环境受限(如嵌入式 JVM),改用
"GBK"(注意:不是"gbk",大小写敏感) - 避免使用
"GB2312"—— 它无法解码“镕”“珮”等常用字,一读就乱
示例片段:
try {
Charset cs = Charset.forName("GB18030");
String content = Files.readString(path, cs);
} catch (UnsupportedCharsetException e) {
// 极少数旧 JDK 可能不带 GB18030,fallback 到 GBK
Charset cs = Charset.forName("GBK");
String content = Files.readString(path, cs);
}
自定义非标准编码(如 HZ、EUC-KR)必须注册 CharsetProvider
如果真要支持 "HZ-GB-2312" 或 "EUC-KR" 这类非 JDK 内置编码,不能靠 Charset.forName() 直接调用,必须:
- 引入对应实现库(如 Apache Commons Codec 提供
HZCodec) - 编写
CharsetProvider子类并打包进META-INF/services/java.nio.charset.spi.CharsetProvider - 确保该 jar 在 classpath 且 provider 类被 ServiceLoader 发现
否则即使代码里写了 Charset.forName("HZ-GB-2312"),也会报 UnsupportedCharsetException。这不是 bug,是 JVM 的安全限制——防止未知 charset 引入解析漏洞。
读取时 charset 不匹配的真实表现和排查线索
乱码不是随机出现的,有迹可循:
- 中文变成问号(
"??")→ 源文件是 UTF-8,却用"ISO-8859-1"读取 - 中文变成方块或小方框 → 源文件是 GBK,却用
"UTF-8"读取(UTF-8 解 GBK 字节流会产生非法序列,被替换为 ) - 部分字正常、部分乱(如“张三丰”变“张?丰”)→ 用了
"GB2312"读含 GB18030 扩展字的文件 -
MalformedInputException或UnmappableCharacterException→ 编码声明与实际不符,需检查 BOM 或文件头
最稳妥的方式永远是:先用工具(如 file -i、enca 或 IDE 的文件编码探测)确认真实编码,再选 Charset.forName() 参数。别猜。


















