【Java 编程】Java 字符集配置及 ObjectMapper 映射 utf8 bom 文件时的错误分析
文章目录
1. Java 读取文件时的字符集配置
utf-8 文本文件 test.txt
{
"name": "shanghai",
"label": "\"上海\""
}
1.1 默认字符集
比如在 Windows 平台,打开 CMD,可以查看本地字符集:
public static void main( String[] args )
{
try ( FileReader r = new FileReader("test.txt")){
System.out.println(r.getEncoding()); // 查看 Java 解析文本所用的字符集
char[] buf = new char[100];
r.read(buf);
System.out.print(buf);
} catch (Exception e){
e.printStackTrace();
}
}
编译程序,生成 demo.jar,执行:
java -jar demo.jar
打印出来的结果为 GBK! 文本解析出现乱码,因为文件编码是 utf-8。
1.2 配置 Java 字符集
可以肯定的是,Java 程序并不会去识别文件本身到底是什么格式,而是根据配置的字符集去解析文本文件。
默认情况下,会读取系统本地的字符集配置,当然也可以自定义。
方法 1、 通过命令行参数配置: -Dfile.encoding
java -jar -Dfile.encoding=UTF-8 demo.jar
文件的解析编码为 UTF8,内容解析正确!
方法 2、通过编码进行配置:
当通过命令行配置时,会影响整个程序解析文本的方式,有时候,想对特定文件指定特定的解码方式,就需要在代码中进行设定了!
Java 中 InputStreamReader 负责将从文件中读取的字节流转换为字符流,可以指定字符集。
代码如下:
String encoding = new InputStreamReader(new FileInputStream("test.txt"), "utf-8").getEncoding()
System.out.println(encoding); // utf-8
1.3 IDEA 调试程序时的陷阱
IDEA 调试程序时,会添加很多参数,包括字符集配置。
可以通过 Jconsole 查看 IDEA 启动的 Java 程序的 VM 参数,如下:
因此,在读取外部文件时,如果该文件是 utf-8 格式,那么通过 IDEA 调试时,读取并解析文件是没有问题的,但当通过 java 命令执行程序时,如果没有指定 -Dfile.encoding
参数,且本地字符集配置的是 GBK,就会出现错误(乱码),而这种错误显得很诡异,因为在测试环境上有问题,本地调试却无法复现!!!!
2. utf-8 与 utf-8 No Bom
首先,UTF-8 不需要 BOM,尽管 Unicode 标准允许在 UTF-8 中使用 BOM。不含 BOM 的 UTF-8 才是标准形式,在 UTF-8 文件中放置 BOM 主要是微软的习惯。
在 Windows 平台,用记事本打开并保存的文件,默认就是 utf-8 bom 文件,bom 文件会在文件头上添加 EF BB BF
字节流。
使用 ultra edit 查看 test.txt
的十六进制内容:
utf-8 no bom
utf-8 bom
使用 ultra edit 可以将文件另存为期望的格式:
此外, IntellJ IDEA 创建和保存的文本文件是 no bom 的,毕竟 Java 是平台无关的!!!
3. ObjectMapper 与 bom
编程语言一般都是平台无关的,因此 bom 文件常常会诱发一些错误!
Java 本身就是平台无关的,所以当 Java 在读取 utf-8 bom 文件时并不会自动忽略掉 Windows 为文件添加的 EF BB BF
文件头,而是将 EF BB BF
视作文件内容。
因此当使用 ObjectMapper 将文件中的 json 字符串转换为 Object 时,utf-8 bom 文件会报错!
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('' (code 65279 / 0xfeff)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
这时,需要将文件转存为 no bom 格式!
测试代码
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.FileReader;
public class Demo
{
public static class Data {
String name;
String label;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString() {
return "Data{" +
"name='" + name + '\'' +
", label='" + label + '\'' +
'}';
}
}
public static void main(String[] args)
{
try ( FileReader r1 = new FileReader("test.txt");
FileReader r2 = new FileReader("test.txt")){
Data d = new ObjectMapper().readValue(r1, Data.class);
System.out.println(d);
System.out.println(r2.getEncoding());
char[] buf = new char[100];
r2.read(buf);
System.out.print(buf);
} catch (Exception e){
e.printStackTrace();
}
}