JAVA字符流与字符集编码
?http://elf8848.iteye.com/blog/271742
問題:
?????? 當用JAVA字符流向硬盤寫一個a.txt文件時,默認情況下a.txt 會使用什么字符集編碼?
?
分析:
???? "字符流"是用JVM中所設置的字符集編碼, 可我也沒設置,那默認是什么字符集呢?
????? JVM是從系統變量file.encoding中讀取操作系統的默認編碼的字符集,來設置JVM的字符集編碼
?
要查看系統的file.encoding參數,可以用以下代碼:
?
Java代碼 ??
結果與操作系統(我用MS Windows)的區域語言有關系,如下表
?
| 中文(中國) | GBK |
| 中文(新加坡) | GBK |
| 中文(香港特別行政區) | MS950 |
| 中文(澳門特別行政區) | MS950 |
| 中文(臺灣) | MS950 |
?
?
我用的簡體中文XP ,? 所以 答案是 GBK
默認情況下a.txt 會使用GBK字符集編碼
我們要想讓a.txt 用UTF-8字符集編碼保存, 就要做如下工作, new OutputStreamWriter() ,指定字符集編碼,如下:
?
Java代碼 ??
?
?
?
?
?
?
?
?
//------------------------------------ 轉兩篇文件 以供參考----------------------------------------------------
?
?
?
?
JVM的默認編碼問題對JSTL標簽的影響
Posted on 2006-03-02 11:17 么么茶 閱讀(384) 評論(1) ?編輯 ?收藏 所屬分類: JAVA-WEB <!---->今日無意中發現在中文系統使用良好的中文頁面在移植到英文系統后出現了亂碼,多方查找發現,出現亂碼的頁面是使用的jstl標簽的c:url和c:param標簽生成的URL通過c:import嵌入網頁的,所以懷疑可能是c:param在進行Base64編碼是使用的編碼有問題。通過查找JSTL的源碼發現在org.apache.taglibs.standard.tag.common.core.ParamSupport類即c:param的實現類中有這樣一行代碼:
???parent.addParameter(URLEncoder.encode(name), URLEncoder.encode(value))
其中URLEncoder.encode是問題所在。在JDK的API文檔中已經標明URLEncoder.encode(String)這個方法已經deprecated了,而應該在編碼時指定編碼字符集。找到這里之后,再來看看URLEncoder.encode(String)的實現:
???public static String encode(String s){
??????String str = null;
??????try {
?????????str = encode(s, dfltEncName);
??????} catch (UnsupportedEncodingException e) {
??????}
??????return str;
???}
可以看到默認使用了dfltEncName的字符集,那么這個dfltEncName又是如何得到的呢?從源碼中找到如下:
???dfltEncName = (String) AccessController.doProivileged(new GetPropertyAction("file.encoding"))
看到這里就一目了然了,JVM是從系統變量file.encoding中讀取了默認編碼的字符集。
之后,寫了一個簡單的測試程序,分別從中英文系統中取了一次默認的字符集,中文沒問題是GBK,但是英文的就很奇怪,居然是Cp1252(好像是Latin I,按說應該是ISO8859-1才是)。看了一下英文系統的區域設置,Location選的是Englist(United States),改成PRC就完事大吉了。
為了防止這種問題的發生,最后還是決定采用最保險的辦法,在Tomcat的啟動參數中加入-Dfile.encoding=GBK,強制將缺省的字符集設為GBK,這樣就一勞永逸了。
//--------------------------------------------------------------------
JAVA編碼轉換的詳細過程
我們常見的JAVA程序包括以下類別:
?????? *直接在console上運行的類(包括可視化界面的類)
?????? *JSP代碼類(注:JSP是Servlets類的變型)
?????? *Servelets類
?????? *EJB類
?????? *其它不可以直接運行的支持類
這些類文件中,都有可能含有中文字符串,并且我們常用前三類JAVA程序和用戶直接交互,用于輸出和輸入字符,如:我們在JSP和Servlet中得到客戶端送來的字符,這些字符也包括中文字符。無論這些JAVA類的作用如何,這些JAVA程序的生命周期都是這樣的:
*編程人員在一定的操作系統上選擇一個合適的編輯軟件來實現源程序代碼并以.java擴展名保存在操作系統中,例如我們在中文win2k中用記事本編輯一個java源程序;
?????? *編程人員用JDK中的javac.exe來編譯這些源代碼,形成.class類(JSP文件是由容器調用JDK來編譯的);
?????? *直接運行這些類或將這些類布署到WEB容器中去運行,并輸出結果。
????? 那么,在這些過程中,JDK和JVM是如何將這些文件如何編碼和解碼并運行的呢?
????? 這里,我們以中文win2k操作系統為例說明JAVA類是如何來編碼和被解碼的。
第一步,我們在中文win2k中用編輯軟件如記事本編寫一個Java源程序文件(包括以上五類JAVA程序),程序文件在保存時默認采用了操作系統默認支持GBK編碼格式(操作系統默認支持的格式為file.encoding格式)形成了一個.java文件,也即,java程序在被編譯前,我們的JAVA源程序文件是采用操作系統默認支持的file.encoding編碼格式保存的,java源程序中含有中文信息字符和英文程序代碼;要查看系統的file.encoding參數,可以用以下代碼:
public class ShowSystemDefaultEncoding {
public static void main(String[] args) {
String encoding = System.getProperty("file.encoding");
System.out.println(encoding);
}}
第二步,我們用JDK的javac.exe文件編譯我們的Java源程序,由于JDK是國際版的,在編譯的時候,如果我們沒有用-encoding參數指定我們的JAVA源程序的編碼格式,則javac.exe首先獲得我們操作系統默認采用的編碼格式,也即在編譯java程序時,若我們不指定源程序文件的編碼格式,JDK首先獲得操作系統的file.encoding參數(它保存的就是操作系統默認的編碼格式,如WIN2k,它的值為GBK),然后JDK就把我們的java源程序從file.encoding編碼格式轉化為JAVA內部默認的UNICODE格式放入內存中。然后,javac把轉換后的unicode格式的文件進行編譯成.class類文件,此時.class文件是UNICODE編碼的,它暫放在內存中,緊接著,JDK將此以UNICODE編碼的編譯后的class文件保存到我們的操作系統中形成我們見到的.class文件。對我們來說,我們最終獲得的.class文件是內容以UNICODE編碼格式保存的類文件,它內部包含我們源程序中的中文字符串,只不過此時它己經由file.encoding格式轉化為UNICODE格式了。這一步中,對于JSP源程序文件是不同的,對于JSP,這個過程是這樣的:即WEB容器調用JSP編譯器,JSP編譯器先查看JSP文件中是否設置有文件編碼格式,如果JSP文件中沒有設置JSP文件的編碼格式,則JSP編譯器調用JDK先把JSP文件用JVM默認的字符編碼格式(也即WEB容器所在的操作系統的默認的file.encoding)轉化為臨時的Servlet類,然后再把它編譯成UNICODE格式的class類,并保存在臨時文件夾中。如:在中文win2k上,WEB容器就把JSP文件從GBK編碼格式轉化為UNICODE格式,然后編譯成臨時保存的Servlet類,以響應用戶的請求。
????? 第三步,運行第二步編譯出來的類,分為三種情況:
????? A、 直接在console上運行的類
????? B、 EJB類和不可以直接運行的支持類(如JavaBean類)
????? C、 JSP代碼和Servlet類
????? D、 JAVA程序和數據庫之間 下面我們分這四種情況來看。
A、直接在console上運行的類
這種情況,運行該類首先需要JVM支持,即操作系統中必須安裝有JRE。運行過程是這樣的:首先java啟動JVM,此時JVM讀出操作系統中保存的class文件并把內容讀入內存中,此時內存中為UNICODE格式的class類,然后JVM運行它,如果此時此類需要接收用戶輸入,則類會默認用file.encoding編碼格式對用戶輸入的串進行編碼并轉化為unicode保存入內存(用戶可以設置輸入流的編碼格式)。程序運行后,產生的字符串(UNICODE編碼的)再回交給JVM,最后JRE把此字符串再轉化為file.encoding格式(用戶可以設置輸出流的編碼格式)傳遞給操作系統顯示接口并輸出到界面上。以上每一步的轉化都需要正確的編碼格式轉化,才能最終不出現亂碼現象。
B、EJB類和不可以直接運行的支持類(如JavaBean類)
由于EJB類和不可以直接運行的支持類,它們一般不與用戶直接交互輸入和輸出,它們常常與其它的類進行交互輸入和輸出,所以它們在第二步被編譯后,就形成了內容是UNICODE編碼的類保存在操作系統中了,以后只要它與其它的類之間的交互在參數傳遞過程中沒有丟失,則它就會正確的運行。 C、JSP代碼和Servlet類
經過第二步后,JSP文件也被轉化為Servlets類文件,只不過它不像標準的Servlets一校存在于classes目錄中,它存在于WEB容器的臨時目錄中,故這一步中我們也把它做為Servlets來看。
對于Servlets,客戶端請求它時,WEB容器調用它的JVM來運行Servlet,首先,JVM把Servlet的class類從系統中讀出并裝入內存中,內存中是以UNICODE編碼的Servlet類的代碼,然后JVM在內存中運行該Servlet類,如果Servlet在運行的過程中,需要接受從客戶端傳來的字符如:表單輸入的值和URL中傳入的值,此時如果程序中沒有設定接受參數時采用的編碼格式,則WEB容器會默認采用ISO-8859-1編碼格式來接受傳入的值并在JVM中轉化為UNICODE格式的保存在WEB容器的內存中。Servlet運行后生成輸出,輸出的字符串是UNICODE格式的,緊接著,容器將Servlet運行產生的UNICODE格式的串(如html語法,用戶輸出的串等)直接發送到客戶端瀏覽器上并輸出給用戶,如果此時指定了發送時輸出的編碼格式,則按指定的編碼格式輸出到瀏覽器上,如果沒有指定,則默認按ISO-8859-1編碼發送到客戶的瀏覽器上。 D、Java程序和數據庫之間
對于幾乎所有數據庫的JDBC驅動程序,默認的在JAVA程序和數據庫之間傳遞數據都是以ISO-8859-1為默認編碼格式的,所以,我們的程序在向數據庫內存儲包含中文的數據時,JDBC首先是把程序內部的UNICODE編碼格式的數據轉化為ISO-8859-1的格式,然后傳遞到數據庫中,在數據庫保存數據時,它默認即以ISO-8859-1保存,所以,這是為什么我們常常在數據庫中讀出的中文數據是亂碼。
?
總結
以上是生活随笔為你收集整理的JAVA字符流与字符集编码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: csv 中显示逗号和双引号
- 下一篇: poi jxl 生成EXCEL 报表