一直在試圖搞清楚java中的編碼問題,也看了網上的一些文章,但仍是雲裏霧裏。直到最近看了方立勳老師的web課程,才略略明白一點。html
在此記錄一下本身的理解,看看本身能不能說清楚。java
第一個問題:我在java代碼中定義了一個字符串,它是什麼編碼?node
字符串實質是一個char數組。那麼char的編碼,其實就是字符串的編碼。那麼char什麼編碼呢?爲何'中'字轉int類型後的值是20013呢?web
char c = '中'; System.out.println(c); // 中 System.out.println((int) c); // 20013
引入第一個概念:Unicode編碼數組
Unicode是一種「編碼」,所謂編碼就是一個編號(數字)到字符的一種映射關係,就僅僅是一種一對一的映射而已。瀏覽器
java的String使用的編碼是Unicode。tomcat
來個簡單的例子證實:服務器
找個轉碼工具,將'中'字轉爲Unicode編碼,結果是'\u4e2d'。用'\u4e2d'替換原來的'中'字。打印的結果和'中'字是同樣的。網絡
char c = '\u4e2d'; System.out.println(c); // 中 System.out.println((int) c); // 20013
'\u'是什麼意思?jsp
'\u'的意思就是使用了Unicode編碼。後面加上十六進制代碼來表示Unicode字符。下面這段代碼能夠驗證。
Integer num = Integer.valueOf("4e2d", 16); System.out.println(num.intValue()); // 20013
第二個問題:編碼和編碼格式的區別是什麼?
這部份內容來自文章 java中的編碼和編碼格式問題 做者:風未馨
1. Unicode是一種「編碼」,所謂編碼就是一個編號(數字)到字符的一種映射關係,就僅僅是一種一對一的映射而已。
2. GBK、UTF-8是一種「編碼格式」,是用來序列化或存儲1中提到的那個「編號(數字)」的一種「格式」。
編碼和編碼格式:
java的String使用的編碼是Unicode,當String存在於內存中時(在代碼中用string類型的引用對它進行操做時),是"只有編碼而沒有編碼格式的",因此java程序中的任何String對象,說它是gbk仍是utf-8都是錯的,String在內存中不須要「編碼格式」, 它只是一個Unicode的字符串而已。
當字符串須要在網絡中傳輸或要被寫入文件時,就須要使用編碼格式了。亂碼問題也所以出現。
GBK和UTF-8:
GBK和UTF-8都是用來序列化或存儲Unicode編碼的數據的,可是分別是2種不一樣的格式,他們都是Unicode編碼的實現方式;他們倆除了格式不同以外,他們所關心的Unicode編碼範圍也不同。
UTF-8考慮了不少種不一樣國家的字符,涵蓋整個unicode碼錶,因此其存儲一個字符的編碼的時候,使用的字節長度也從1字節到4字節不等;
而GBK只考慮中文——在Unicode中的一小部分的字符的編碼,因此它算好了只要2個字節就能涵蓋到絕大多數經常使用中文(2個字節能表示6w多種字符),因此它存儲一個字符的時候,所用的字節長度是固定的;
ASCII碼和Unicode:
ASCII碼,和Unicode編碼同樣,也是一種"編碼"。
ASCII碼的範圍比較小,一共規定了128個字符的編碼。
Unicode編碼是一個很大的集合,如今的規模能夠容納100多萬個符號。就像它的名字都表示的,這是一種包含全部符號的編碼。
固然也有其餘的編碼,沒用過,我也不甚瞭解。
第三個問題:哪些地方會用到編碼格式?
前文中提到過一句話:當字符串須要在網絡中傳輸或要被寫入文件時,就須要使用編碼格式了。
網絡中傳輸:
對於java web開發人員,指的就是java web了。那在java web中,哪些地方須要設置編碼格式呢?
先上個圖(網上隨便拷貝來的)。假定瀏覽器就是ie,WEB服務器是tomcat,頁面是jsp,應用服務器用的是servlet。
1. 瀏覽器中打開了一個頁面,這個頁面映射應用服務的某個jsp。此時瀏覽器中的解碼格式是什麼呢?
瀏覽器的解碼格式是在jsp中指定的,好比在jsp文件中常常能夠看到這樣兩行代碼。
<!--
這一句是和Tomcat說的:保存在硬盤上的jsp文件在被Tomcat翻譯成servlet的時候,使用utf-8解碼jsp文件的內容。
若是不指定,會默認使用iso-8859-1來解碼。
-->
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!-- 這一行是和瀏覽器說的:瀏覽器解碼的時候請使用utf-8解碼哦 -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
這樣瀏覽在解析這個jsp的時候,就是使用utf-8編碼來解碼。
瀏覽器的解碼格式也是能夠在瀏覽器上設置的:
若是網頁中出現了亂碼,你換一種編碼格式來解碼,可能就不亂了。
2. response中也能夠設置內容的編碼格式和指定瀏覽器的解碼格式。
開發人員應該都知道,jsp就是一個特殊一點的servlet。在servlet中, response有兩個設置編碼的方法,和jsp中的那兩行編碼配置有着類似的功能。
它們就是:
// 表示response的內容會以utf-8的編碼方式編碼後發送給瀏覽器。
response.setCharacterEncoding("UTF-8");
// 告訴瀏覽器,解碼的時候也要使用utf-8解碼哦。 response.setContentType("text/html;charset=UTF-8");
3. 經過request對象能夠指定應用服務用哪一種編碼格式來解碼接收到的數據.
上面兩種狀況,都是講servlet本身如何編碼,而後告訴瀏覽器如何解碼的。servlet也能夠指定對接收到的對象使用哪一種編碼格式來解碼。
// 經過這句話,能夠指定用utf-8編碼格式來解碼瀏覽器傳來的數據。不過只對post方式傳來的數據有效。若是是get方法傳來的數據,仍是會以默認的iso-8859-1來解碼。
request.setCharacterEncoding("UTF-8");
4. 設置tomcat服務器配置文件server.xml,指定以何種編碼解碼瀏覽器傳來的參數。
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" />
'URIEncoding="UTF-8"', 這個屬性的配置, 和"request.setCharacterEncoding("UTF-8");"這句代碼的功能大體相同。都是指定tomcat服務器接受到收據後如何解碼。
若是不指定,tomcat服務器會默認使用iso-8859-1來解碼。
此處建議不要使用URIEncoding指定編碼,由於一個tomcat服務器裏可能會有多個應用,由於某一個應用修改tomcat配置可能會致使其餘應用出錯,編碼轉換在每一個應用中單獨處理便可。
被寫入文件:
1. 文件內容在被寫入文件時是被編碼了的,默認的編碼格式是gb2312(可能和系統有關,簡體中文系統測試,就是gb2312)。
能夠在代碼中指定文件流在寫入文件時用什麼編碼格式。好比指定用"utf-8".
Writer write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
2. 文件自己不一樣軟件是有選擇性的支持能夠對哪些編碼進行解碼。
好比:
Excel支持gb2312,不支持UTF-8。
Txt記事本支持UTF-8編碼。
因此有時候,我下載一個csv文件(指定內容使用utf-8編碼)。會遇到這樣的狀況:
這個csv文件在使用excel打開的時候,中文是亂碼的,若是換成txt打開,中文就正常顯示了。
這個時候若是想要在excel中不亂碼,就須要在代碼中指定內容使用gb2312編碼。
也能夠經過txt的'另存爲',改變文本的編碼格式。
3. 如何肯定文件的編碼呢?
我在win7(簡體中文)系統中,右鍵->新建一個文本文檔,nodepad++打開後,查看編碼,能夠看到編碼是ANSI。
使用java代碼生成一個txt文件,未明確指定編碼格式,nodepad++打開後,查看編碼,能夠看到編碼是utf-8。
Eclipse中作了一個小測試:
test1. 指定utf-8編碼格式
String fullPath = "D:\\test2.txt"; File file = new File(fullPath); if (!file.exists()) { file.createNewFile(); } Writer write = new OutputStreamWriter(new FileOutputStream(file), "utf-8"); write.write("你好"); write.flush(); write.close();
生成的文件編碼格式是:
test2. 指定gb2312編碼格式
String fullPath = "D:\\test2.txt"; File file = new File(fullPath); if (!file.exists()) { file.createNewFile(); } Writer write = new OutputStreamWriter(new FileOutputStream(file), "gb2312"); write.write("你好"); write.flush(); write.close();
生成的文件編碼格式是:
test3. 先使用utf-8生成文件,輸出"你好",再使用gb2312,追加內容,輸出"你好",結果是這樣子的。
總結:
第一次使用utf-8編碼格式輸出你好,此時文件內容 第一個"你好" 是utf-8編碼的,文件的解碼方式也是utf-8.
第二次使用gb2312編碼格式輸出你好,此時的文件追加的內容 第二個"你好" 是gb2312編碼的,文件的解碼方式也變成了gb2312。
也就是說,Eclipse操做文本文件TXT時,txt文件會一直使用最後一次操做文件使用的編碼格式來解碼文件的內容。
因此第二次輸出"你好"後,第一次輸出的"你好"變成了亂碼。