java課程設計例子
Java如何獲取文件編碼格式
1:簡單判斷是UTF-8或不是UTF-8,因為一般除了UTF-8之外就是GBK,所以就設置默認為GBK。
按照給定的字元集存儲文件時,在文件的最開頭的三個位元組中就有可能存儲著編碼信息,所以,基本的原理就是只要讀出文件前三個位元組,判定這些位元組的值,就可以得知其編碼的格式。其實,如果項目運行的平台就是中文操作系統,如果這些文本文件在項目內產生,即開發人員可以控制文本的編碼格式,只要判定兩種常見的編碼就可以了:GBK和UTF-8。由於中文Windows默認的編碼是GBK,所以一般只要判定UTF-8編碼格式。
對於UTF-8編碼格式的文本文件,其前3個位元組的值就是-17、-69、-65,所以,判定是否是UTF-8編碼格式的代碼片段如下:
File file = new File(path);
InputStream in= new java.io.FileInputStream(file);
byte[] b = new byte[3];
in.read(b);
in.close();
if (b[0] == -17 && b[1] == -69 && b[2] == -65)
System.out.println(file.getName() + ":編碼為UTF-8");
else
System.out.println(file.getName() + ":可能是GBK,也可能是其他編碼");
2:若想實現更復雜的文件編碼檢測,可以使用一個開源項目cpdetector,它所在的網址是:http://cpdetector.sourceforge.net/。它的類庫很小,只有500K左右,cpDetector是基於統計學原理的,不保證完全正確,利用該類庫判定文本文件的代碼如下:
讀外部文件(先利用cpdetector檢測文件的編碼格式,然後用檢測到的編碼方式去讀文件):
/**
* 利用第三方開源包cpdetector獲取文件編碼格式
*
* @param path
* 要判斷文件編碼格式的源文件的路徑
* @author huanglei
* @version 2012-7-12 14:05
*/
public static String getFileEncode(String path) {
/*
* detector是探測器,它把探測任務交給具體的探測實現類的實例完成。
* cpDetector內置了一些常用的探測實現類,這些探測實現類的實例可以通過add方法 加進來,如ParsingDetector、
* JChardetFacade、ASCIIDetector、UnicodeDetector。
* detector按照「誰最先返回非空的探測結果,就以該結果為准」的原則返回探測到的
* 字元集編碼。使用需要用到三個第三方JAR包:antlr.jar、chardet.jar和cpdetector.jar
* cpDetector是基於統計學原理的,不保證完全正確。
*/
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
/*
* ParsingDetector可用於檢查HTML、XML等文件或字元流的編碼,構造方法中的參數用於
* 指示是否顯示探測過程的詳細信息,為false不顯示。
*/
detector.add(new ParsingDetector(false));
/*
* JChardetFacade封裝了由Mozilla組織提供的JChardet,它可以完成大多數文件的編碼
* 測定。所以,一般有了這個探測器就可滿足大多數項目的要求,如果你還不放心,可以
* 再多加幾個探測器,比如下面的ASCIIDetector、UnicodeDetector等。
*/
detector.add(JChardetFacade.getInstance());// 用到antlr.jar、chardet.jar
// ASCIIDetector用於ASCII編碼測定
detector.add(ASCIIDetector.getInstance());
// UnicodeDetector用於Unicode家族編碼的測定
detector.add(UnicodeDetector.getInstance());
java.nio.charset.Charset charset = null;
File f = new File(path);
try {
charset = detector.detectCodepage(f.toURI().toURL());
} catch (Exception ex) {
ex.printStackTrace();
}
if (charset != null)
return charset.name();
else
return null;
}
String charsetName = getFileEncode(configFilePath);
System.out.println(charsetName);
inputStream = new FileInputStream(configFile);
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, charsetName));
讀jar包內部資源文件(先利用cpdetector檢測jar內部的資源文件的編碼格式,然後以檢測到的編碼方式去讀文件):
/**
* 利用第三方開源包cpdetector獲取URL對應的文件編碼
*
* @param path
* 要判斷文件編碼格式的源文件的URL
* @author huanglei
* @version 2012-7-12 14:05
*/
public static String getFileEncode(URL url) {
/*
* detector是探測器,它把探測任務交給具體的探測實現類的實例完成。
* cpDetector內置了一些常用的探測實現類,這些探測實現類的實例可以通過add方法 加進來,如ParsingDetector、
* JChardetFacade、ASCIIDetector、UnicodeDetector。
* detector按照「誰最先返回非空的探測結果,就以該結果為准」的原則返回探測到的
* 字元集編碼。使用需要用到三個第三方JAR包:antlr.jar、chardet.jar和cpdetector.jar
* cpDetector是基於統計學原理的,不保證完全正確。
*/
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
/*
* ParsingDetector可用於檢查HTML、XML等文件或字元流的編碼,構造方法中的參數用於
* 指示是否顯示探測過程的詳細信息,為false不顯示。
*/
detector.add(new ParsingDetector(false));
/*
* JChardetFacade封裝了由Mozilla組織提供的JChardet,它可以完成大多數文件的編碼
* 測定。所以,一般有了這個探測器就可滿足大多數項目的要求,如果你還不放心,可以
* 再多加幾個探測器,比如下面的ASCIIDetector、UnicodeDetector等。
*/
detector.add(JChardetFacade.getInstance());// 用到antlr.jar、chardet.jar
// ASCIIDetector用於ASCII編碼測定
detector.add(ASCIIDetector.getInstance());
// UnicodeDetector用於Unicode家族編碼的測定
detector.add(UnicodeDetector.getInstance());
java.nio.charset.Charset charset = null;
try {
charset = detector.detectCodepage(url);
} catch (Exception ex) {
ex.printStackTrace();
}
if (charset != null)
return charset.name();
else
return null;
}
URL url = CreateStationTreeModel.class.getResource("/resource/" + "配置文件");
URLConnection urlConnection = url.openConnection();
inputStream=urlConnection.getInputStream();
String charsetName = getFileEncode(url);
System.out.println(charsetName);
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, charsetName));
3:探測任意輸入的文本流的編碼,方法是調用其重載形式:
charset=detector.detectCodepage(待測的文本輸入流,測量該流所需的讀入位元組數);
上面的位元組數由程序員指定,位元組數越多,判定越准確,當然時間也花得越長。要注意,位元組數的指定不能超過文本流的最大長度。
4:判定文件編碼的具體應用舉例:
屬性文件(.properties)是Java程序中的常用文本存儲方式,象STRUTS框架就是利用屬性文件存儲程序中的字元串資源。它的內容如下所示:
#注釋語句
屬性名=屬性值
讀入屬性文件的一般方法是:
FileInputStream ios=new FileInputStream(「屬性文件名」);
Properties prop=new Properties();
prop.load(ios);
String value=prop.getProperty(「屬性名」);
ios.close();
利用java.io.Properties的load方法讀入屬性文件雖然方便,但如果屬性文件中有中文,在讀入之後就會發現出現亂碼現象。發生這個原因是load方法使用位元組流讀入文本,在讀入後需要將位元組流編碼成為字元串,而它使用的編碼是「iso-8859-1」,這個字元集是ASCII碼字元集,不支持中文編碼,
方法一:使用顯式的轉碼:
String value=prop.getProperty(「屬性名」);
String encValue=new String(value.getBytes(「iso-8859-1″),」屬性文件的實際編碼」);
方法二:象這種屬性文件是項目內部的,我們可以控制屬性文件的編碼格式,比如約定採用Windows內定的GBK,就直接利用」gbk」來轉碼, 如果約定採用UTF-8,就使用」UTF-8″直接轉碼。
方法三:如果想靈活一些,做到自動探測編碼,就可利用上面介紹的方法測定屬性文件的編碼,從而方便開發人員的工作
補充:可以用下面代碼獲得Java支持編碼集合:
Charset.availableCharsets().keySet();
可以用下面的代碼獲得系統默認編碼:
Charset.defaultCharset();
2. java中filewriter中文亂碼怎麼解決
要明白,用filewriter讀取文件採取的是平台默認編碼(視操作系統而定)。當要寫入的文本文件編碼和平台默認編碼不一致時,就會出現中文亂碼的情況。這時可以使用filewriter 的父類OutputStreamWriter來讀取。OutputStreamWriter允許用戶指定編碼方式,代碼為:
FileInputStream fis=new FileInputStream("文件路徑");
OutputStreamWriter osw=new OutputStreamWriter(fis,"文本文件的編碼方式(ANSI,UTF-8...)");
osw.write();
這樣寫入的編碼可以保證和源文本文件編碼一致,就不會出現亂碼了。。。
3. java中編碼與解碼分別指什麼
問題一:在java中讀取文件時應該採用什麼編碼?
Java讀取文件的方式總體可以分為兩類:按位元組讀取和按字元讀取。按位元組讀取就是採用InputStream.read()方法來讀取位元組,然後保存到一個byte[]數組中,最後經常用new String(byte[]);把位元組數組轉換成String。在最後一步隱藏了一個編碼的細節,new String(byte[]);會使用操作系統默認的字元集來解碼位元組數組,中文操作系統就是GBK。而我們從輸入流里讀取的位元組很可能就不是GBK編碼的,因為從輸入流里讀取的位元組編碼取決於被讀取的文件自身的編碼。舉個例子:我們在D:盤新建一個名為demo.txt的文件,寫入」我們。」,並保存。此時demo.txt編碼是ANSI,中文操作系統下就是GBK。此時我們用輸入位元組流讀取該文件所得到的位元組就是使用GBK方式編碼的位元組。那麼我們最終new String(byte[]);時採用平台默認的GBK來編碼成String也是沒有問題的(位元組編碼和默認解碼一致)。試想一下,如果在保存demo.txt文件時,我們選擇UTF-8編碼,那麼該文件的編碼就不在是ANSI了,而變成了UTF-8。仍然採用輸入位元組流來讀取,那麼此時讀取的位元組和上一次就不一樣了,這次的位元組是UTF-8編碼的位元組。兩次的位元組顯然不一樣,一個很明顯的區別就是:GBK每個漢字兩個位元組,而UTF-8每個漢字三個位元組。如何我們最後還使用new String(byte[]);來構造String對象,則會出現亂碼,原因很簡單,因為構造時採用的默認解碼GBK,而我們的位元組是UTF-8位元組。正確的辦法就是使用new String(byte[],」UTF-8」);來構造String對象。此時我們的位元組編碼和構造使用的解碼是一致的,不會出現亂碼問題了。
說完位元組輸入流,再來說說位元組輸出流。
我們知道如果採用位元組輸出流把位元組輸出到某個文件,我們是無法指定生成文件的編碼的(假設文件以前不存在),那麼生成的文件是什麼編碼的呢?經過測試發現,其實這取決於寫入的位元組編碼格式。比如以下代碼:
OutputStream out = new FileOutputStream("d:\\demo.txt");
out.write("我們".getBytes());
getBytes()會採用操作系統默認的字元集來編碼位元組,這里就是GBK,所以我們寫入demo.txt文件的是GBK編碼的位元組。那麼這個文件的編碼就是GBK。如果稍微修改一下程序:out.write("我們".getBytes(「UTF-8」));此時我們寫入的位元組就是UTF-8的,那麼demo.txt文件編碼就是UTF-8。這里還有一點,如果把」我們」換成123或abc之類的ascii碼字元,那麼無論是採用getBytes()或者getBytes(「UTF-8」)那麼生成的文件都將是GBK編碼的。
這里可以總結一下,InputStream中的位元組編碼取決文件本身的編碼,而OutputStream生成文件的編碼取決於位元組的編碼。
下面說說採用字元輸入流來讀取文件。
首先,我們需要理解一下字元流。其實字元流可以看做是一種包裝流,它的底層還是採用位元組流來讀取位元組,然後它使用指定的編碼方式將讀取位元組解碼為字元。說起字元流,不得不提的就是InputStreamReader。以下是java api對它的說明: InputStreamReader是位元組流通向字元流的橋梁:它使用指定的charset 讀取位元組並將其解碼為字元。它使用的字元集可以由名稱指定或顯式給定,否則可能接受平台默認的字元集。說到這里其實很明白了,InputStreamReader在底層還是採用位元組流來讀取位元組,讀取位元組後它需要一個編碼格式來解碼讀取的位元組,如果我們在構造InputStreamReader沒有傳入編碼方式,那麼會採用操作系統默認的GBK來解碼讀取的位元組。還用上面demo.txt的例子,假設demo.txt編碼方式為GBK,我們使用如下代碼來讀取文件:
InputStreamReader in = new InputStreamReader(new FileInputStream(「demo.txt」));
那麼我們讀取不會產生亂碼,因為文件採用GBK編碼,所以讀出的位元組也是GBK編碼的,而InputStreamReader默認採用解碼也是GBK。如果把demo.txt編碼方式換成UTF-8,那麼我們採用這種方式讀取就會產生亂碼。這是因為位元組編碼(UTF-8)和我們的解碼編碼(GBK)造成的。解決辦法如下:
InputStreamReader in = new InputStreamReader(new FileInputStream(「demo.txt」),」UTF-8」);
給InputStreamReader指定解碼編碼,這樣二者統一就不會出現亂碼了。
下面說說字元輸出流。
字元輸出流的原理和字元輸入流的原理一樣,也可以看做是包裝流,其底層還是採用位元組輸出流來寫文件。只是字元輸出流根據指定的編碼將字元轉換為位元組的。字元輸出流的主要類是:OutputStreamWriter。Java api解釋如下:OutputStreamWriter 是字元流通向位元組流的橋梁:使用指定的 charset 將要向其寫入的字元編碼為位元組。它使用的字元集可以由名稱指定或顯式給定,否則可能接受平台默認的字元集。說的很明白了,它需要一個編碼將寫入的字元轉換為位元組,如果沒有指定則採用GBK編碼,那麼輸出的位元組都將是GBK編碼,生成的文件也是GBK編碼的。如果採用以下方式構造OutputStreamWriter:
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(「dd.txt」),」UTF-8」);
那麼寫入的字元將被編碼為UTF-8的位元組,生成的文件也將是UTF-8格式的。
問題二: 既然讀文件要使用和文件編碼一致的編碼,那麼javac編譯文件也需要讀取文件,它使用什麼編碼呢?
這個問題從來就沒想過,也從沒當做是什麼問題。正是因為問題一而引發的思考,其實這里還是有東西可以挖掘的。下面分三種情況來探討,這三種情況也是我們常用的編譯java源文件的方法。
1.javac在控制台編譯java類文件。
通常我們手動建立一個java文件Demo.java,並保存。此時Demo.java文件的編碼為ANSI,中文操作系統下就是GBK.然後使用javac命令來編譯該源文件。」javac Demo.java」。Javac也需要讀取java文件,那麼javac是使用什麼編碼來解碼我們讀取的位元組呢?其實javac採用了操作系統默認的GBK編碼解碼我們讀取的位元組,這個編碼正好也是Demo.java文件的編碼,二者一致,所以不會出現亂碼情況。讓我們來做點手腳,在保存Demo.java文件時,我們選擇UTF-8保存。此時Demo.java文件編碼就是UTF-8了。我們再使用」javac Demo.java」來編譯,如果Demo.java里含有中文字元,此時控制台會出現警告信息,也出現了亂碼。究其原因,就是因為javac採用了GBK編碼解碼我們讀取的位元組。因為我們的位元組是UTF-8編碼的,所以會出現亂碼。如果不信的話你可以自己試試。那麼解決辦法呢?解決辦法就是使用javac的encoding參數來制定我們的解碼編碼。如下:javac -encoding UTF-8 Demo.java。這里我們指定了使用UTF-8來解碼讀取的位元組,由於這個編碼和Demo.java文件編碼一致,所以不會出現亂碼情況了。
2.Eclipse中編譯java文件。
我習慣把Eclipse的編碼設置成UTF-8。那麼每個項目中的java源文件的編碼就是UTF-8。這樣編譯也從沒有問題,也沒有出現過亂碼。正是因為這樣才掩蓋了使用javac可能出現的亂碼。那麼Eclipse是如何正確編譯文件編碼為UTF-8的java源文件的呢?唯一的解釋就是Eclipse自動識別了我們java源文件的文件編碼,然後採取了正確的encoding參數來編譯我們的java源文件。功勞都歸功於IDE的強大了。
3.使用Ant來編譯java文件。
Ant也是我常用的編譯java文件的工具。首先,必須知道Ant在後台其實也是採用javac來編譯java源文件的,那麼可想而知,1會出現的問題在Ant中也會存在。如果我們使用Ant來編譯UTF-8編碼的java源文件,並且不指定如何編碼,那麼也會出現亂碼的情況。所以Ant的編譯命令<javac>有一個屬性」 encoding」允許我們指定編碼,如果我們要編譯源文件編碼為UTF-8的java文件,那麼我們的命令應該如下:
<javac destdir="${classes}" target="1.4" source="1.4" deprecation="off" debug="on" debuglevel="lines,vars,source" optimize="off" encoding="UTF-8">
指定了編碼也就相當於」javac –encoding」了,所以不會出現亂碼了。
問題三:tomcat中編譯jsp的情況。
這個話題也是由問題二引出的。既然javac編譯java源文件需要採用正確的編碼,那麼tomcat編譯jsp時也要讀取文件,此時tomcat採用什麼編碼來讀取文件?會出現亂碼情況嗎?下面我們來分析。
我們通常會在jsp開頭寫上如下代碼:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
我常常不寫pageEncoding這個屬於,也不明白它的作用,但是不寫也沒出現過亂碼情況。其實這個屬性就是告訴tomcat採用什麼編碼來讀取jsp文件的。它應該和jsp文件本身的編碼一致。比如我們新建個jsp文件,設置文件編碼為GBK,那麼此時我們的pageEncoding應該設置為GBK,這樣我們寫入文件的字元就是GBK編碼的,tomcat讀取文件時採用也是GBK編碼,所以能保證正確的解碼讀取的位元組。不會出現亂碼。如果把pageEncoding設置為UTF-8,那麼讀取jsp文件過程中轉碼就出現了亂碼。上面說我常常不寫pageEncoding這個屬性,但是也沒出現過亂碼,這是怎麼回事呢?那是因為如果沒有pageEncoding屬性,tomcat會採用contentType中charset編碼來讀取jsp文件,我的jsp文件編碼通常設置為UTF-8,contentType的charset也設置為UTF-8,這樣tomcat使用UTF-8編碼來解碼讀取的jsp文件,二者編碼一致也不會出現亂碼。這只是contentType中charset的一個作用,它還有兩個作用,後面再說。可能有人會問:如果我既不設置pageEncoding屬性,也不設置contentType的charset屬性,那麼tomcat會採取什麼編碼來解碼讀取的jsp文件呢?答案是iso-8859-1,這是tomcat讀取文件採用的默認編碼,如果用這種編碼來讀取文件顯然會出現亂碼。
問題四:輸出。
問題二和問題三分析的過程其實就是從源文件àclass文件過程中的轉碼情況。最終的class文件都是以unicode編碼的,我們前面所做的工作就是把各種不同的編碼轉換為unicode編碼,比如從GBK轉換為unicode,從UTF-8轉換為unicode。因為只有採用正確的編碼來轉碼才能保證不出現亂碼。Jvm在運行時其內部都是採用unicode編碼的,其實在輸出時,又會做一次編碼的轉換。讓我們分兩種情況來討論。
1.java中採用Sysout.out.println輸出。
比如:Sysout.out.println(「我們」)。經過正確的解碼後」我們」是unicode保存在內存中的,但是在向標准輸出(控制台)輸出時,jvm又做了一次轉碼,它會採用操作系統默認編碼(中文操作系統是GBK),將內存中的unicode編碼轉換為GBK編碼,然後輸出到控制台。因為我們操作系統是中文系統,所以往終端顯示設備上列印字元時使用的也是GBK編碼。因為終端的編碼無法手動改變,所以這個過程對我們來說是透明的,只要編譯時能正確轉碼,最終的輸出都將是正確的,不會出現亂碼。在Eclipse中可以設置控制台的字元編碼,具體位置在Run Configuration對話框的Common標簽里,我們可以試著設置為UTF-8,此時的輸出就是亂碼了。因為輸出時是採用GBK編碼的,而顯示卻是使用UTF-8,編碼不同,所以出現亂碼。
2.jsp中使用out.println()輸出到客戶端瀏覽器。
Jsp編譯成class後,如果輸出到客戶端,也有個轉碼的過程。Java會採用操作系統默認的編碼來轉碼,那麼tomcat採用什麼編碼來轉碼呢?其實tomcat是根據<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>中contentType的charset參數來轉碼的,contentType用來設置tomcat往瀏覽器發送HTML內容所使用的編碼。Tomcat根據這個編碼來轉碼內存中的unicode。經過轉碼後tomcat輸出到客戶端的字元編碼就是utf-8了。那麼瀏覽器怎麼知道採取什麼編碼格式來顯示接收到的內容呢?這就是contentType的charset屬性的第三個作用了:這個編碼會在HTTP響應頭中指定以通知瀏覽器。瀏覽器使用http響應頭的contentType的charset屬性來顯示接收到的內容。
總結一下contentType charset的三個作用:
1).在沒有pageEncoding屬性時,tomcat使用它來解碼讀取的jsp文件。
2).tomcat向客戶端輸出時,使用它來編碼發送的內容。
3).通知瀏覽器,應該以什麼編碼來顯示接收到的內容。
為了能更好的理解上面所說的解碼和轉碼過程,我們舉一個例子。
新建一個index.jsp文件,該文件編碼為GBK,在jsp開頭我們寫上如下代碼:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="GBK"%>
這里的charset和pageEncoding不同,但是也不會出現亂碼,我來解釋一下。首先tomcat讀取jsp內容,並根據pageEncoding指定的GBK編碼將讀取的GBK位元組解碼並轉換為unicode位元組碼保存在class文件中。然後tomcat在輸出時(out.println())使用charset屬性將內存中的unicode轉換為utf-8編碼,並在響應頭中通知瀏覽器,瀏覽器以utf-8顯示接收到的內容。整個過程沒有一次轉碼錯誤,所以就不會出現亂碼情況。
問題五:Properties和ResourceBundle使用的解碼編碼。
以上兩個是我們常用的類,他們在讀取文件過程中並不允許我們指定解碼編碼,那麼它們採取什麼解碼方式呢?查看源碼後發現都是採用iso-8859-1編碼來解碼
的。這樣的話我們也不難理解我們寫的properties文件為什麼都是iso-8859-1 的了。因為採取任何一個別的編碼都將產生亂碼。因為iso-8859-1編碼是沒
有中文的,所以我們輸入的中文要轉換為unicode,通常我們使用插件來完成,也可以使用jdk自帶的native2ascii工具。
4. Java:Java的class文件採用utf8的編碼方式,Java的字元串是unicode編碼的
Java中的class文件編碼方式與源碼文件的編碼格式有所不同。class文件的編碼方式通常是UTF-8,這是一種廣泛使用的Unicode編碼方式,能夠支持幾乎所有的字元集。
而在Java程序中,字元串的編碼是Unicode。Java虛擬機(JVM)在處理字元串時,會將所有的字元存儲為16位的Unicode字元。這樣可以確保字元串可以包含任何Unicode字元,包括非ASCII字元。
使用UTF-8編碼的class文件能夠保證在不同平台上的一致性,而Unicode編碼的字元串則使得Java程序能夠處理全球范圍內的文本。
對於開發者而言,這兩者之間的區別主要體現在文件存儲和程序內部處理上。在編寫源代碼時,推薦使用UTF-8編碼,因為它不僅支持多種字元集,還能確保兼容性。而在程序中操作字元串時,Java內置的支持Unicode使得處理各種字元變得簡單。
綜上所述,class文件採用UTF-8編碼,而Java中的字元串則使用Unicode編碼,這保證了Java程序能夠處理和存儲各種字元,同時也確保了跨平台的兼容性。