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