摘要:一規則下的漢字使用編碼方式的文件,一個漢字所占用的是三個字節,而其他字母控制字符之類還是按照的編碼方式,即占一個字節。
一、UTF-8規則下的漢字
使用UTF-8編碼方式的文件,一個漢字所占用的是三個字節(byte),而其他字母控制字符之類還是按照ASCII的編碼方式,即占一個字節。為了在解碼的時候區分,經對三千個常用漢字的測試發現,在漢字所占用的三個字節當中:
一個字節轉換為10進制的范圍為:[-28 ~ -23]
第二個字節和第三個字節的10進制范圍均為:[-128 ~ -65]
???????這樣在比如new String(byte[] b)類的函數在解碼的時候,就能夠通過字節為正來判斷是ASCII字符,如是在[-28 ~ -23]范圍內則是一個漢字的開始,并且后面還有兩個[-128 ~ -65]范圍的字節時,就會把這三個字節轉換為一個漢字。
測試代碼如下:
public class FileInputStreamTest { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("srcIO系統漢字"); List> listTreeSet =new ArrayList<>(); for(int i=0;i<3;i++) listTreeSet.add(new TreeSet ()); byte[] bbuf = new byte[3]; int hasRead = 0; while ((hasRead = fis.read(bbuf)) > 0 ) { //System.out.println(new String(bbuf , 0 , hasRead ));//可用來查看當前bbuf字節數組表示的一個漢字 for(int i=0;i<3;i++) if(bbuf[i]<0) { //這里用了這一句是因為eclispe里面一行有長度限制,所以將漢字分成了三行,因為每行末換行符和回車符各占一個字節,再在下行初加上一個字母比如a就可以使得三個字符被一起讀走而不影響漢字字節的讀取順序,其他的每三個字符仍然是一個漢字 listTreeSet.get(i).add((int) bbuf[i]); } } for(int i=0;i<3;i++) System.out.println("第"+(i+1)+"個字節:"+listTreeSet.get(i)); fis.close(); } } /** 第1個字節:[-28, -27, -26, -25, -24, -23] 第2個字節:[-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65] 第3個字節:[-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65] **/
java的char類型,只有兩個字節,在采用utf-8編碼時,為什么可以存儲漢字?:
Unicode是一種字符集(charset),即字符的集合。UTF-8與UTF-16都是是一種建立在Unicode字符集上面的編碼方式(encoding),是將Unicode字符集里的字符轉換成具體的二進制流。所不同的是在UTF-8和UTF-16當中,將Unicode中一個漢字編碼成二進制后,分別是三個字節大小和兩個字節大小。
在一個Java文件(例如該文件為UTF-8編碼)里面寫上這樣一句話char a = "猿";如圖所示,編譯后生成的class文件是UTF-8的,不過是modified的(可能與通常的utf-8的機制有些許區別),一個漢字仍然是占三個字節的,但關鍵在于運行的時候會將其轉換為UTF-16編碼方式下的,這樣在運行的時候char類型當中仍然只放有兩個字節,所以java編譯器也是允許用char來存放中文字符的。
詳細參考回答java編譯器編碼和JVM編碼問題?
關于字符集與編碼方式的關系:字符集就是字符的集合,如ASCII,GBK,BIG5,Unicode等,編碼方式是即可理解為定義在字符集上的映射規則。
對于unicode字符集,有utf8,utf16,utf32等多種編碼方式,但對于其他字符集,只有一種默認的編碼方式:比如,ASCII,GBK,GB2312等,不僅僅代表字符集,同時也代表了(默認的)的編碼方式。
在win10中默認的字符集是gb2312,。用notepad++隨便打開一個txt文件,在右下角就能看到字符集
而我自己在eclipse里面設置的編碼是utf-8(對應字符集是不同的,所以)
所以對于我們在windows記事本中創建的txt文件直接復制到eclipse中就會出現亂碼,如下所示,因為兩個文件采用了不同的字符集,發生了亂碼:
而且經過測試這種GB2312與UTF-8之間的相互轉換的效果是不可逆的(因為發生了信息丟失),代碼如下:
public class TestCharset { public static void main(String[] args) throws IOException { Charset utf8 = StandardCharsets.UTF_8; Charset gbk2312 = Charset.forName("GB2312"); //將某段文字以gb2312編碼后得到的字節數組,再以utf-8進行解碼得到的文字是亂碼,并且這段亂碼中丟失了信息。 //所以不能再轉換回utf-8了 ByteBuffer BytesExpressTextOnGBK2312 = gbk2312.encode("天生我才必有用"); CharBuffer Decode_BytesExpressTextOnGBK2312_UseUTF8 = utf8.decode(BytesExpressTextOnGBK2312); System.out.println("將"天生我才必有用"按照GBK2312規則編碼后得到的字節數組,再以UTF8解碼得到的文字: "+Decode_BytesExpressTextOnGBK2312_UseUTF8); ByteBuffer Encode__Decode_BytesExpressTextOnGBK2312_UseUTF8__UseUTF8 = utf8.encode(Decode_BytesExpressTextOnGBK2312_UseUTF8); CharBuffer Decode___Encode__Decode_BytesExpressTextOnGBK2312_UseUTF8__UseUTF8___UseGBK2312 = gbk2312.decode(Encode__Decode_BytesExpressTextOnGBK2312_UseUTF8__UseUTF8); System.out.println("將上面的文字再反向以UTF8編碼得到的字節數組,再按照GBK2312解碼得到的文字: "+Decode___Encode__Decode_BytesExpressTextOnGBK2312_UseUTF8__UseUTF8___UseGBK2312); System.out.println("-----------------------------------------------分割線-----------------------------------------------"); //同樣 將某段文字以utf8編碼后得到的字節數組,再以utf-8進行解碼得到的文字是亂碼,并且這段亂碼中丟失了信息 //逆向后大部分文字也不能恢復,不過比上面的完全不能恢復好了一些 ByteBuffer BytesExpressTextOnUTF8 = utf8.encode("天生我才必有用"); CharBuffer Decode_BytesExpressTextOnUTF8_UseGBK2312 = gbk2312.decode(BytesExpressTextOnUTF8); System.out.println("將"天生我才必有用"按照UTF8規則編碼后得到的字節數組,再以GBK2312解碼得到的文字: "+Decode_BytesExpressTextOnUTF8_UseGBK2312); ByteBuffer Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312= gbk2312.encode(Decode_BytesExpressTextOnUTF8_UseGBK2312); CharBuffer Decode___Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312___UseUTF8 = utf8.decode(Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312); System.out.println("將上面的文字再反向以GBK2312編碼得到的字節數組,再按照UTF8解碼得到的文字: "+Decode___Encode__Decode_BytesExpressTextOnUTF8_UseGBK2312__UseGBk2312___UseUTF8); } } /** 將"天生我才必有用"按照GBK2312規則編碼后得到的字節數組,再以UTF8解碼得到的文字: ???????????? 將上面的文字再反向以UTF8編碼得到的字節數組,再按照GBK2312解碼得到的文字: 錕斤拷錕斤拷錕揭才憋拷錕斤拷錕斤拷 -----------------------------------------------分割線----------------------------------------------- 將"天生我才必有用"按照UTF8規則編碼后得到的字節數組,再以GBK2312解碼得到的文字: 澶╃??????蹇????? 將上面的文字再反向以GBK2312編碼得到的字節數組,再按照UTF8解碼得到的文字: 天????????????? **/復制文字時可能不會出現亂碼
那為什么我們直接從txt復制文字過去就不會變成亂碼呢?
目前找到的比較信服的觀點是:對于復制粘貼文字而言,虛擬機軟件(比如我們的java虛擬機)、遠程主機軟件都會有一個「介于兩系統之間的」剪貼板,「連接起」這兩個系統的各自剪貼板,并做一些編碼格式轉換的工作。這樣我們復制過去的文字就肯定不會出現亂碼了。
按照這個說法,推測就是說eclispe當中會有一個針對windows系統的剪貼板,比如當我們從txt中復制過去的文字(實際上應該是字節數組)與eclispe中的我們設置的編碼不同的話,先會按照txt原本的編碼規則對字節數組進行解碼得到文字,然后再按照eclispe我們設置的編碼規則進行編碼,這然后就可以粘貼過去而不出錯了。但也只是一種推測可能原理不是這樣,但肯定的是做了一些編碼轉換的工作。
擴展文章:Java編碼問題原因以及解決
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/72377.html
摘要:所以中國人自己創造了一種字符編碼,每個漢字和符號用兩個字節來表示。第一個字節稱為高位字節,第二個字節稱為低位字節。而目前為止我們使用最廣泛的中文編碼還是。 網站開發中經常會被亂碼問題困擾。知道文件編碼錯誤會導致亂碼,但對其中的原理卻知之甚少。偶然從某篇文章了解了Unicode,發現從這條線出發也牽引出了一系列缺失的知識點。通過研讀文章,基本了解了一些以前不明白的問題,所以整理了幾篇,從...
摘要:只包含了個基本拉丁字母阿拉伯數目字和英式標點符號一共個字符,因此只需要不占滿一個字節就可以存儲,而則涵蓋的數據除了視覺上的字形編碼方法標準的字符編碼外,還包含了字符特性,如大小寫字母,共可包含個字符,而到現在只填充了其中的個位置。 項目地址:https://git.io/pytips 0x07 和 0x08 分別介紹了 Python 中的字符串類型(str)和字節類型(byte),以及...
閱讀 1456·2021-09-02 19:23
閱讀 1603·2021-08-11 11:19
閱讀 649·2019-08-30 15:55
閱讀 1661·2019-08-30 12:50
閱讀 2248·2019-08-30 11:23
閱讀 2188·2019-08-29 13:13
閱讀 1510·2019-08-28 18:13
閱讀 3347·2019-08-26 11:53