【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();
}
}