可以通過如下代碼獲得默認編碼:
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、成都微信小程序、集團企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了石獅免費建站歡迎大家使用!
System.out.println(Charset.defaultCharset());
字符編碼(英語:Character encoding)也稱字集碼,是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式、自然數(shù)序列、8位組或者電脈沖),以便文本在計算機中存儲和通過通信網(wǎng)絡(luò)的傳遞。常見的例子包括將拉丁字母表編碼成摩斯電碼和ASCII。其中,ASCII將字母、數(shù)字和其它符號編號,并用7比特的二進制來表示這個整數(shù)。通常會額外使用一個擴充的比特,以便于以1個字節(jié)的方式存儲。
在計算機技術(shù)發(fā)展的早期,如ASCII(1963年)和EBCDIC(1964年)這樣的字符集逐漸成為標準。但這些字符集的局限很快就變得明顯,于是人們開發(fā)了許多方法來擴展它們。對于支持包括東亞CJK字符家族在內(nèi)的寫作系統(tǒng)的要求能支持更大量的字符,并且需要一種系統(tǒng)而不是臨時的方法實現(xiàn)這些字符的編碼。
這種編碼問題真是很tricky的問題。說它tricky是因為這至少涉及到以下4種編碼選取的排列組合(有時甚至更多),更有時乃至會發(fā)生錯進錯出,負負得正,中間過程錯了但反而到不是亂碼的情況。
(1)源代碼的編碼
(2)編譯時告訴java編譯器的源代碼編碼
(3)運行時jvm參數(shù)file.encoding
(4)輸出終端對輸出字節(jié)流的解碼所采用的碼組
在這簡單情況下(1)和(2)一致,(3)和(4)一致就不會因為編解碼映射錯誤(當然字符向終端字體映射的錯誤是另一回事,如字體缺失之類)。而(1)(2)和(3)(4)不必一致,這樣就使得不必強求開發(fā)編譯環(huán)境和運行應(yīng)用環(huán)境的編碼必須一致。
源代碼的錄入與編譯若在在一個平臺上時,大多數(shù)情況沒有問題(反而用聰明的Idea IDE設(shè)置錯誤時會亂套,越是簡陋的開發(fā)環(huán)境越不太會錯)。但是如果你在中文GBK編碼平臺上的源代碼在別人的unicode編碼平臺上編譯,就有問題了。所以和別人,特別是和不同母語的人合作編程時,建議要么約定一律用unicode作為源文件編碼;要么只用ASCII字符,反正其他編碼一般都和ASCII兼容的,對于非ASCII字符,用Java的/uxxxx表示機制,比如"中國"就表示為"\u4e2d\u56fd"。4e2d和56fd分別是中國二字的unicode十六進制編碼。
但我認為樓主在這里其實主要關(guān)心的是運行時的編碼一致問題,即(3)和(4)。所以言歸正傳,讓我們來檢查它們是否一致。
由于正如上述,iso8859-1編碼集其實是被其他所有公認的編碼集所兼容的,也就是說它是所有公認編碼集的公共子集。所以以iso8859-1為基礎(chǔ)可以外延到任何一個公認編碼集。事實上大多數(shù)情況也是這樣做的。比如java System property里設(shè)定了encoding為iso8859-1,事實上不僅僅是一個Latin字母的映射,在非Latin區(qū)域按JVM宿主操作系統(tǒng)的編碼擴展。即選iso8859-1其實是選擇了宿主操作系統(tǒng)的默認編碼。
假設(shè)樓主的操作系統(tǒng)編碼是GBK,那么file.encoding=iso8859-1相當于選擇了file.encoding=GBK。那么System.out.println(...)這個核心類方法會將china字符轉(zhuǎn)換為file.encoding指定的編碼(GBK)字節(jié)由out流輸出給最終out所綁定的終端。比如console一般采用系統(tǒng)默認編碼也是GBK的話,那就和file.encoding一致,能正常解碼,不會亂碼。
至于System.out.write()直接寫字節(jié)流。由于該字節(jié)流是由china.getBytes()得到的,在不指定編碼的時候使用file.encoding指定的默認值的(即GBK),因此Str-Byte的編碼方法GBK和console采用的解碼方法GBK又是一致的,所以也不是亂碼。
但是這時候用toHexString打印出的兩個字節(jié)串是不一樣的。先直接把china逐字強行轉(zhuǎn)換為int的情況,不涉及輸出編碼,總是unicode的。(JVM規(guī)范規(guī)定class里字串必須unicode編碼)只要上述(1) (2)匹配,java編譯器會自動從各種編碼的源文件正確轉(zhuǎn)成class文件里統(tǒng)一unicode編碼的字串。相反,作為一個題外話提一下,當(1)(2)不匹配時會在特定的一種配合(1)(2)的(3)(4)也不匹配的情況下會負負得正輸出正常,但這是絕對錯誤的做法,因為任何要求(1)(2)和(3)(4)有匹配關(guān)系的要求都是在應(yīng)用中可能無法滿足的。java編譯器對這種情況也會報告warning,但不fail。
綜上,一旦file.encoding設(shè)成宿主操作系統(tǒng)默認而系統(tǒng)consle也采用操作系統(tǒng)默認編解碼的話,(3)(4)總是一致的,無論系統(tǒng)選擇的是GBK還是utf-8等等。
那么如果file.encoding不選系統(tǒng)默認呢?比如utf-8。那就很可能出現(xiàn)亂碼了。但是,慢著,試驗的結(jié)果還是沒有亂碼。那是因為file.encoding是靜態(tài)的JVM系統(tǒng)參數(shù),在程序里像樓主那樣設(shè)定是不起作用的(我不知道有沒有辦法發(fā)一個什么通知讓這種程序改變生效的)。必須作為JVM參數(shù)直接傳給java程序讓它構(gòu)造虛擬機的時候就得到這個參數(shù),否則JVM會去拿宿主系統(tǒng)的默認值,就相當于又回到設(shè)file.encoding=iso8859-1了。
java -Dfile.encoding=utf-8 A
這下終于亂碼了,而且兩個都亂了。打印出的字節(jié)串一個還是unicode,另一個從GBK變到utf-8了。
如果你發(fā)現(xiàn)試驗的現(xiàn)象和我上面說的正好相反,請注意檢查console的編碼設(shè)置,我們上面假設(shè)它也采用了宿主系統(tǒng)默認編碼,但有些console很高級的嘞,可以設(shè)置成不通編碼的(其實幾乎所有的都可以)。那么分析的方法和上面一樣,結(jié)果可能正好相反。
public
byte[]
getBytes(String
charsetName)
使用指定的字符集將此String編碼為byte序列,結(jié)果存在一個byte數(shù)組中
public
String(byte[]
bytes,
String
charsetName)
通過使用指定的
charset
解碼指定的
byte
數(shù)組,構(gòu)造一個新的
String。
在網(wǎng)絡(luò)傳輸中,信息都是以字節(jié)序列的方式傳輸?shù)摹K?,發(fā)送方的String要按照某種編碼方式(如UTF-8,GBK)編碼為字節(jié)序列,在網(wǎng)絡(luò)中傳輸后,接收方取得這個字節(jié)序列,按照相同的編碼方式將字節(jié)序列解碼為String。
請看下面的代碼片段:
String
name
=
"張三";
byte[]
b1
=
name.getBytes("UTF-8");
String
name1
=
new
String(b1,
"UTF-8");
//編碼解碼相同,正常顯示
System.out.println(name1);
String
name2
=
new
String(b1,
"GBK");
//編碼解碼不同,亂碼
System.out.println(name2);
byte[]
b2
=
name.getBytes("GBK");
String
name3
=
new
String(b2,
"GBK");
//編碼解碼相同,正常顯示
System.out.println(name3);
String
name4
=
new
String(b2,
"UTF-8");
//編碼解碼不同,亂碼
System.out.println(name4);
至于你的那個情況,要先用gbk編碼,然后再用utf-8解碼才能獲得正常的字符串,我估計是因為
1.傳輸過來的字節(jié)碼是用utf-8編碼的,假設(shè)字節(jié)碼為b。
2.你獲得的那個字符串,假設(shè)為s,是用gbk對b進行解碼獲得的字符串,所以是亂碼。
3.你使用gbk對s進行編碼,用gbk解碼之后再編碼,于是獲得了原來的b。
4.你使用utf-8解碼,所以獲得了正常的字符串。
簡單的說:
b
-
(gbk解碼)
-
亂碼
-
[此處開始是你做的](gbk編碼)
-
b
-
(utf-8解碼)
-
正常字符串
研究完編碼收獲會不小的,對以后理解Java的輸入輸出(尤其是網(wǎng)絡(luò)通信和文件讀寫)都很有幫助。
1:簡單判斷是UTF-8或不是UTF-8,因為一般除了UTF-8之外就是GBK,所以就設(shè)置默認為GBK。
按照給定的字符集存儲文件時,在文件的最開頭的三個字節(jié)中就有可能存儲著編碼信息,所以,基本的原理就是只要讀出文件前三個字節(jié),判定這些字節(jié)的值,就可以得知其編碼的格式。其實,如果項目運行的平臺就是中文操作系統(tǒng),如果這些文本文件在項目內(nèi)產(chǎn)生,即開發(fā)人員可以控制文本的編碼格式,只要判定兩種常見的編碼就可以了:GBK和UTF-8。由于中文Windows默認的編碼是GBK,所以一般只要判定UTF-8編碼格式。
對于UTF-8編碼格式的文本文件,其前3個字節(jié)的值就是-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:若想實現(xiàn)更復(fù)雜的文件編碼檢測,可以使用一個開源項目cpdetector,它所在的網(wǎng)址是:。它的類庫很小,只有500K左右,cpDetector是基于統(tǒng)計學原理的,不保證完全正確,利用該類庫判定文本文件的代碼如下:
讀外部文件(先利用cpdetector檢測文件的編碼格式,然后用檢測到的編碼方式去讀文件):
/**
* 利用第三方開源包cpdetector獲取文件編碼格式
*
* @param path
* 要判斷文件編碼格式的源文件的路徑
* @author huanglei
* @version 2012-7-12 14:05
*/
public static String getFileEncode(String path) {
/*
* detector是探測器,它把探測任務(wù)交給具體的探測實現(xiàn)類的實例完成。
* cpDetector內(nèi)置了一些常用的探測實現(xiàn)類,這些探測實現(xiàn)類的實例可以通過add方法 加進來,如ParsingDetector、
* JChardetFacade、ASCIIDetector、UnicodeDetector。
* detector按照“誰最先返回非空的探測結(jié)果,就以該結(jié)果為準”的原則返回探測到的
* 字符集編碼。使用需要用到三個第三方JAR包:antlr.jar、chardet.jar和cpdetector.jar
* cpDetector是基于統(tǒng)計學原理的,不保證完全正確。
*/
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
/*
* ParsingDetector可用于檢查HTML、XML等文件或字符流的編碼,構(gòu)造方法中的參數(shù)用于
* 指示是否顯示探測過程的詳細信息,為false不顯示。
*/
detector.add(new ParsingDetector(false));
/*
* JChardetFacade封裝了由Mozilla組織提供的JChardet,它可以完成大多數(shù)文件的編碼
* 測定。所以,一般有了這個探測器就可滿足大多數(shù)項目的要求,如果你還不放心,可以
* 再多加幾個探測器,比如下面的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包內(nèi)部資源文件(先利用cpdetector檢測jar內(nèi)部的資源文件的編碼格式,然后以檢測到的編碼方式去讀文件):
/**
* 利用第三方開源包cpdetector獲取URL對應(yīng)的文件編碼
*
* @param path
* 要判斷文件編碼格式的源文件的URL
* @author huanglei
* @version 2012-7-12 14:05
*/
public static String getFileEncode(URL url) {
/*
* detector是探測器,它把探測任務(wù)交給具體的探測實現(xiàn)類的實例完成。
* cpDetector內(nèi)置了一些常用的探測實現(xiàn)類,這些探測實現(xiàn)類的實例可以通過add方法 加進來,如ParsingDetector、
* JChardetFacade、ASCIIDetector、UnicodeDetector。
* detector按照“誰最先返回非空的探測結(jié)果,就以該結(jié)果為準”的原則返回探測到的
* 字符集編碼。使用需要用到三個第三方JAR包:antlr.jar、chardet.jar和cpdetector.jar
* cpDetector是基于統(tǒng)計學原理的,不保證完全正確。
*/
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
/*
* ParsingDetector可用于檢查HTML、XML等文件或字符流的編碼,構(gòu)造方法中的參數(shù)用于
* 指示是否顯示探測過程的詳細信息,為false不顯示。
*/
detector.add(new ParsingDetector(false));
/*
* JChardetFacade封裝了由Mozilla組織提供的JChardet,它可以完成大多數(shù)文件的編碼
* 測定。所以,一般有了這個探測器就可滿足大多數(shù)項目的要求,如果你還不放心,可以
* 再多加幾個探測器,比如下面的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:探測任意輸入的文本流的編碼,方法是調(diào)用其重載形式:
charset=detector.detectCodepage(待測的文本輸入流,測量該流所需的讀入字節(jié)數(shù));
上面的字節(jié)數(shù)由程序員指定,字節(jié)數(shù)越多,判定越準確,當然時間也花得越長。要注意,字節(jié)數(shù)的指定不能超過文本流的最大長度。
4:判定文件編碼的具體應(yīng)用舉例:
屬性文件(.properties)是Java程序中的常用文本存儲方式,象STRUTS框架就是利用屬性文件存儲程序中的字符串資源。它的內(nèi)容如下所示:
#注釋語句
屬性名=屬性值
讀入屬性文件的一般方法是:
FileInputStream ios=new FileInputStream(“屬性文件名”);
Properties prop=new Properties();
prop.load(ios);
String value=prop.getProperty(“屬性名”);
ios.close();
利用java.io.Properties的load方法讀入屬性文件雖然方便,但如果屬性文件中有中文,在讀入之后就會發(fā)現(xiàn)出現(xiàn)亂碼現(xiàn)象。發(fā)生這個原因是load方法使用字節(jié)流讀入文本,在讀入后需要將字節(jié)流編碼成為字符串,而它使用的編碼是“iso-8859-1”,這個字符集是ASCII碼字符集,不支持中文編碼,
方法一:使用顯式的轉(zhuǎn)碼:
String value=prop.getProperty(“屬性名”);
String encValue=new String(value.getBytes(“iso-8859-1″),”屬性文件的實際編碼”);
方法二:象這種屬性文件是項目內(nèi)部的,我們可以控制屬性文件的編碼格式,比如約定采用Windows內(nèi)定的GBK,就直接利用”gbk”來轉(zhuǎn)碼, 如果約定采用UTF-8,就使用”UTF-8″直接轉(zhuǎn)碼。
方法三:如果想靈活一些,做到自動探測編碼,就可利用上面介紹的方法測定屬性文件的編碼,從而方便開發(fā)人員的工作
補充:可以用下面代碼獲得Java支持編碼集合:
Charset.availableCharsets().keySet();
可以用下面的代碼獲得系統(tǒng)默認編碼:
Charset.defaultCharset();