java中编码问题_[干货预警]一次搞懂Java中的编码问题
編碼問題一直是一個(gè)困擾程序員的問題,尤其是對(duì)于java程序員。因?yàn)閖ava的跨平臺(tái)特性,經(jīng)常需要在多個(gè)編碼之間進(jìn)行轉(zhuǎn)換。
下面詳細(xì)講一講java中的編碼問題
一、為什么要編碼
長(zhǎng)話短說,原因如下:
1,計(jì)算機(jī)存儲(chǔ)信息的最小單位是字節(jié)Byte。占8個(gè)二進(jìn)制位。所以一個(gè)字節(jié)能表示的狀態(tài)只有255中
2,人類語言的符號(hào)太多,255位不夠
3,所以必須把多個(gè)字節(jié)合起來表示一個(gè)人類語言符號(hào)
4,在怎么組合字節(jié)上出現(xiàn)了多種方法,這就是編碼
二、常用編碼格式
現(xiàn)在的編碼格式有很多,常見的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等。
能表示漢字的有GB2312、GBK、UTF-8、UTF-16 這幾種。不同的編碼格式在存儲(chǔ)空間和編碼效率方面都有不同。
如何選擇,需要多方考慮。
先簡(jiǎn)單介紹一下這幾種編碼。
ASCII 碼
最基礎(chǔ)的編碼格式,標(biāo)準(zhǔn)ASCII碼只有127個(gè)。連一個(gè)字節(jié)都沒用完,只用了低7位。
后來又除了擴(kuò)展ASCII碼把第8位利用了起來。不過總的來說,能表示的字符非常有限。
僅適用于英語系語種
屬于單子節(jié)編碼格式。
ISO-8859-1
這個(gè)就是上面說的擴(kuò)展ASCII碼。向下兼容標(biāo)準(zhǔn)ASCII碼
雖然只能表示256個(gè)字符,但是已經(jīng)包含了絕大多數(shù)西歐語言,所以使用上還是非常廣泛的。
GB2312
全稱是《信息交換用漢字編碼字符集 基本集》,請(qǐng)注意這個(gè)基本字眼。它是雙字節(jié)編碼,總的編碼范圍是 A1-F7,其中從 A1-A9 是符號(hào)區(qū),總共包含 682 個(gè)符號(hào),從 B0-F7 是漢字區(qū),包含 6763 個(gè)漢字。
GBK
全稱叫《漢字內(nèi)碼擴(kuò)展規(guī)范》。另一個(gè)名字叫擴(kuò)展GB2312。所以你就知道了,GBK是GB2312的擴(kuò)展,能表示 21003 個(gè)漢字。向下兼容GB2312,所以用GB2312編碼的漢字用GBK來解碼是沒有問題的。
GBK是雙字節(jié)的,不論編碼英文字母還是漢字都是雙字節(jié)的。
GB18030(兼容GB2312)
全稱是《信息交換用漢字編碼字符集》,是我國(guó)的強(qiáng)制標(biāo)準(zhǔn),它是可變字節(jié)的,可能是單字節(jié)、雙字節(jié)或者四字節(jié)編碼,它的編碼與 GB2312 編碼兼容,這個(gè)雖然是國(guó)家標(biāo)準(zhǔn),但是實(shí)際系統(tǒng)中使用的并不廣泛。
Unicode 編碼方案
看了這么多編碼,很煩對(duì)吧。所以國(guó)際標(biāo)準(zhǔn)化組織ISO制定了Unicode 編碼方案,可以容納世界上所有文字和符號(hào)的字符編碼。
這里要多說一句,Unicode僅僅是一個(gè)字符集,規(guī)定了一個(gè)人類字符對(duì)應(yīng)的二進(jìn)制數(shù)。至于這個(gè)二進(jìn)制數(shù)的存儲(chǔ)方案,是不管的,由其他開發(fā)者實(shí)現(xiàn)。這個(gè)組織只出標(biāo)準(zhǔn),不出實(shí)現(xiàn)。
所以流行的Unicode 編碼方案有兩種UTF-16和UTF-8。其實(shí)還有一個(gè)UTF-32,用的少。。
UTF-32
32很好理解,就是32位,定長(zhǎng)4個(gè)字節(jié)。不管什么字符,統(tǒng)統(tǒng)32位。
好處好壞處很明顯。效率高(不需要做任何編碼計(jì)算轉(zhuǎn)換),但是浪費(fèi)空間。空間換時(shí)間。
UTF-8
這個(gè)8不是8位的意思,而是最少8位。UTF-8是一種變長(zhǎng)的編碼方式。使用 1~6 個(gè)字節(jié)來存儲(chǔ);
對(duì)于英語等西歐語系,非常喜歡UTF-8。因?yàn)樗麄冎恍枰粋€(gè)字節(jié)即可,
在單子節(jié)的情況下和 ASCII 編碼完全一樣,因此 UTF-8 是兼容 ASCII 的
但是對(duì)于中日韓語系來說,就比較難受,不僅要忍受效率的損失,而且存儲(chǔ)空間也沒有節(jié)省
UTF-16
UTF-16是介于兩者之間的一個(gè)編碼格式。算是一種折中。折中編碼會(huì)固定2字節(jié)或者4字節(jié)
具體來說是對(duì)于 Unicode 編號(hào)范圍在 0000 ~ FFFF 之間的字符,UTF-16 使用兩個(gè)字節(jié)存儲(chǔ),并且直接存儲(chǔ) Unicode 編號(hào),不用進(jìn)行編碼轉(zhuǎn)換,這跟 UTF-32 非常類似。
對(duì)于 Unicode 編號(hào)范圍在 10000~10FFFF 之間的字符,UTF-16 使用四個(gè)字節(jié)存儲(chǔ)
注意,只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因?yàn)樗鼈儧]有單字節(jié)編碼。
所以 UTF-8 和 UTF-16/32 都各有優(yōu)缺點(diǎn),因此選擇的時(shí)候應(yīng)當(dāng)立足于實(shí)際的應(yīng)用場(chǎng)景。實(shí)際上,我們?cè)谄胀ǖ膽?yīng)用開發(fā)中,基本不太關(guān)系這個(gè)。99%的場(chǎng)景是直接使用了UTF-8。我認(rèn)為這并沒有多大問題,UTF-8的變長(zhǎng)特性,決定了以后即使繼續(xù)增加人類語言字符,utf-8都可以放下,反觀utf-16/32可能會(huì)有問題。
Java中的編解碼
編碼解碼主要發(fā)生在字符和字節(jié)的轉(zhuǎn)換過程中,這個(gè)場(chǎng)景一般就是IO,包括磁盤IO和網(wǎng)絡(luò)IO
io中的編解碼
解碼就是由字節(jié)到字符的轉(zhuǎn)換,如從網(wǎng)絡(luò)上拿來的二進(jìn)制流,轉(zhuǎn)換成漢字。
Reader 類是 Java 的 I/O 中讀字符的父類,而 InputStream 類是讀字節(jié)的父類,InputStreamReader 類就是關(guān)聯(lián)字節(jié)到字符的橋梁,它負(fù)責(zé)在 I/O 過程中處理讀取字節(jié)到字符的轉(zhuǎn)換,而具體字節(jié)到字符的解碼實(shí)現(xiàn)它由 StreamDecoder 去實(shí)現(xiàn),在 StreamDecoder 解碼過程中必須由用戶指定 Charset 編碼格式。值得注意的是如果你沒有指定 Charset,將使用本地環(huán)境中的默認(rèn)字符集,例如在中文環(huán)境中將使用 GBK 編碼。
寫的情況也是類似,寫字符的父類是 Writer,寫字節(jié)的父類是 OutputStream,通過 OutputStreamWriter 轉(zhuǎn)換字符到字節(jié)。如下圖所示:
同樣 StreamEncoder 類負(fù)責(zé)將字符編碼成字節(jié),編碼格式和默認(rèn)編碼規(guī)則與解碼是一致的。
寫個(gè)代碼測(cè)試一下:
public class IOTest {@Test public void testWrite() throws Exception { String file = "d:/test1.txt"; String charset = "UTF-8"; // 寫字符換轉(zhuǎn)成字節(jié)流 FileOutputStream outputStream = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter( outputStream, charset); try { writer.write("保存點(diǎn)中文字符"); } finally { writer.close(); } } @Test public void testRead() throws Exception { String file = "d:/test1.txt"; String charset = "UTF-8"; // 讀取字節(jié)轉(zhuǎn)換成字符 FileInputStream inputStream = new FileInputStream(file); InputStreamReader reader = new InputStreamReader( inputStream, charset); StringBuffer buffer = new StringBuffer(); char[] buf = new char[64]; int count = 0; try { while ((count = reader.read(buf)) != -1) { buffer.append(buf, 0, count); } } finally { reader.close(); } System.out.println(buffer.toString()); }}
你可以改下字符集,多測(cè)幾次。讀寫使用不同的字符集會(huì)亂碼。
我們?cè)趯懘a的時(shí)候,一定要手動(dòng)指定下字符集,不要使用默認(rèn)。雖然在一臺(tái)機(jī)器上,不會(huì)出問題。一旦涉及到跨機(jī)器可能有異常。
我們可以看下默認(rèn)的字符集到底是啥。
這是jdk的源碼。在vm啟動(dòng)時(shí),找有沒有配file.encoding參數(shù),配了,去找對(duì)應(yīng)的字符集。如果字符集比較特殊,在不同的機(jī)器上有可能找不到的。否則就是UTF-8
/*** Returns the default charset of this Java virtual machine. * *
The default charset is determined during virtual-machine startup and * typically depends upon the locale and charset of the underlying * operating system. * * @return A charset object for the default charset * * @since 1.5 */public static Charset defaultCharset() { if (defaultCharset == null) { synchronized (Charset.class) { String csn = AccessController.doPrivileged( new GetPropertyAction("file.encoding")); Charset cs = lookup(csn); if (cs != null) defaultCharset = cs; else defaultCharset = forName("UTF-8"); } } return defaultCharset;}
java代碼中的編解碼
除了io之外,我們也經(jīng)常在代碼中進(jìn)行string和byte的轉(zhuǎn)換。
String s = "中文字符串";byte[] b = s.getBytes("UTF-8");String n = new String(b,"UTF-8");
隨手指定字符集是個(gè)好習(xí)慣
總結(jié)
以上是生活随笔為你收集整理的java中编码问题_[干货预警]一次搞懂Java中的编码问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: in会让mysql索引失效吗_mysql
- 下一篇: 计算机科技专业前景,美国伊利诺伊理工大学