關(guān)于編碼格式的小紀(jì)

寫在前面

編解碼的問題是個(gè)蠻大的概念迷雪,網(wǎng)上也搜過一些資料,講的也都蠻詳細(xì)的僵刮,寫此一篇也是給自己做個(gè)紀(jì)要递礼,以免日后再遇見編解碼問題無法解決

遇見問題

在項(xiàng)目中使用的dubbo來做服務(wù)間通信的方式涂臣,有一個(gè)需求是將一個(gè)圖片流顯示給前端盾计,由于項(xiàng)目依賴層級(jí)的原因售担,這個(gè)圖片流只能在服務(wù)的最底層生成,然后向上傳遞署辉,最后交給調(diào)用者族铆。一開始就簡(jiǎn)單的將輸出流inputStream作為返回值傳出去了,結(jié)果報(bào)錯(cuò),大意是不識(shí)別某個(gè)字符哭尝,剛開始沒想到是編碼格式的問題哥攘,以為就是不能傳數(shù)據(jù)流,就想著要不把流轉(zhuǎn)成string傳出去材鹦,結(jié)果試了之后dubbo倒是沒報(bào)錯(cuò)逝淹,生成的圖片無法打開,debug發(fā)現(xiàn)桶唐,底層生成的inputStream的數(shù)據(jù)長(zhǎng)度跟最后string轉(zhuǎn)成的inputStream的長(zhǎng)度不相同栅葡,才想到可能是編碼格式的問題。

尋求解答

其實(shí)想到是編碼格式的問題莽红,第一反應(yīng)是設(shè)置一個(gè)編碼格式好了妥畏,于是給轉(zhuǎn)碼和解碼都加了UTF-8格式的要求邦邦,結(jié)果一看仍然是不對(duì)的安吁,這就有點(diǎn)懵逼了,因?yàn)楸旧韺?duì)這塊不是很熟燃辖,就想著那要不就稍微研究一下是為什么好了鬼店。

首先是原理

從原理上來說,計(jì)算機(jī)都是以二進(jìn)制來儲(chǔ)存所有的數(shù)據(jù)的黔龟,那要表達(dá)全世界各種各樣的字符妇智,就需要有相應(yīng)的編碼格式,告訴計(jì)算機(jī)要以什么樣的規(guī)則來解析一段指定的二進(jìn)制數(shù)據(jù)氏身。而計(jì)算機(jī)在讀取二進(jìn)制數(shù)據(jù)的時(shí)候又規(guī)定了以八個(gè)二進(jìn)制位為一個(gè)字節(jié)巍棱,即1byte,由于每位二進(jìn)制可以有0和1兩種形式蛋欣,所以1byte可以表示256種不同含義航徙。那用不同的編碼格式來讀取同樣的二進(jìn)制,可以得到不同的結(jié)果陷虎,比如同樣的1000100011110001到踏,這是十六位二進(jìn)制數(shù)據(jù),以ISO-8859-1解析和以UTF-8來解析尚猿,結(jié)果就是完全不一樣的意思(當(dāng)然我不知道這具體是啥意思窝稿,隨手寫的,保證結(jié)果不一樣而已)凿掂。這也就是為什么伴榔,同樣的一份txt文件,可以是一堆亂碼,也可以是正常顯示的文字的原因踪少。

然后是原因

上面講了一些計(jì)算機(jī)讀取文件時(shí)顯示具體內(nèi)容的原理骗灶,提到了編碼格式,現(xiàn)在為人熟知且常用的就那幾種秉馏,ASCII耙旦,ISO-8859-1,UTF-8,UTF-16,GBK萝究。這里面有兩組是很相似的免都,一個(gè)是ASCII和ISO-8859-1,一個(gè)是UTF-8和UTF-16帆竹。首先是ASCII和ISO-8859-1绕娘,同樣都是采用單字節(jié)讀取,即用一個(gè)字節(jié)表示一個(gè)字母栽连,不同的是ASCII表只能表示128個(gè)字符险领,因?yàn)樽罡呶皇?不變的;而ISO-8859-1會(huì)用到最高位的值秒紧,所以能夠表示256種不同的字符绢陌。而UTF-8和UTF-16則是Unicode編碼的實(shí)現(xiàn),這里就不得不講一下Unicode編碼和UTF-8熔恢,UTF-16的區(qū)別脐湾。后兩者的區(qū)別比較簡(jiǎn)單,UTF-8采用的是1~4個(gè)字節(jié)不定長(zhǎng)的存儲(chǔ)格式叙淌,而UTF-16則是兩個(gè)字節(jié)或四個(gè)字節(jié)來存儲(chǔ)字符秤掌。Unicode是我們熟知的編碼字符集,而其實(shí)也只是字符集鹰霍,只規(guī)定了符號(hào)的二級(jí)制代碼闻鉴,并沒有規(guī)定如何存儲(chǔ)。什么意思茂洒,比如一個(gè)“漢”字孟岛,Unicode編碼為6C49,轉(zhuǎn)化成二進(jìn)制是1101100 01001001获黔,這并不意味著計(jì)算機(jī)存儲(chǔ)這個(gè)“漢”字就是這些二進(jìn)制蚀苛,還取決于具體的編碼格式。所以通俗點(diǎn)說可以將Unicode理解為接口玷氏,而UTF-8和UTF-16理解為是接口的具體實(shí)現(xiàn)堵未。
而上面這個(gè)“漢”字是如何存儲(chǔ)的呢,比如我們采用UTF-8的方式來存儲(chǔ)盏触,UTF-8定義了兩個(gè)簡(jiǎn)單的編碼規(guī)則:

  • 對(duì)于單字節(jié)的符號(hào)渗蟹,字節(jié)第一位設(shè)0块饺,后面的是這個(gè)符號(hào)的Unicode編碼。所以對(duì)于英文字母雌芽,UTF-8編碼和ASCII碼是一樣的授艰。
  • 對(duì)于n字節(jié)的符號(hào)(n > 1),第一個(gè)字節(jié)的前n位都設(shè)為1世落,第n + 1位設(shè)為0淮腾,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進(jìn)制位屉佳,全部為這個(gè)符號(hào)的Unicode碼谷朝。

Unicode符號(hào)范圍 | UTF-8編碼方式
(十六進(jìn)制) | (二進(jìn)制)
----------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

上面是具體的Unicode轉(zhuǎn)UTF-8的編碼規(guī)則,根據(jù)這張表武花,可以很輕松的定位不同的Unicode編碼對(duì)應(yīng)的UTF-8的編碼格式圆凰,由于“漢”的Unicode編碼是6C49,落在了上面的第三個(gè)區(qū)間,所以UTF-8編碼格式則是111001101011000110001001,即用三個(gè)字節(jié)來儲(chǔ)存体箕。

接著是實(shí)踐

了解了上面的理論专钉,就隨手實(shí)踐一下,看看結(jié)果累铅。首先是想一下情景跃须,上面問題中提到的是從流轉(zhuǎn)成字符,然后從字符再轉(zhuǎn)成流争群,所以模擬一下這個(gè)過程

    byte[] bytes = new byte[2];
    bytes[0] = (byte) 130;
    bytes[1] = 88;
    String test = new String(bytes);
    System.out.print(test);

上面這段代碼模式的是字節(jié)流轉(zhuǎn)成字符流的過程回怜,注意byte[0]需要強(qiáng)轉(zhuǎn)而1不需要大年,是因?yàn)镴ava默認(rèn)一個(gè)byte的大小是0~127换薄,最高位是符號(hào)位,而130由于大于127翔试,則按照規(guī)定轻要,是取互補(bǔ)的那個(gè)負(fù)數(shù),即-126(如果說設(shè)置的是大于256垦缅,則一個(gè)字節(jié)無法表示冲泥,需要多個(gè)字節(jié),這里面就需要更多的運(yùn)算了壁涎,Java則是取最低的那8位)凡恍。然后new一個(gè)string出來,這里沒有設(shè)置編碼格式怔球,Java默認(rèn)使用的是UTF-8(可以通過System.out.println(Charset.defaultCharset());來打印默認(rèn)的編碼格式)嚼酝,打印出來的結(jié)果是?X,可以看見有兩個(gè)字符,第一個(gè)是亂碼竟坛,第二個(gè)識(shí)別出來是X闽巩,亂碼是因?yàn)檫@個(gè)二進(jìn)制流按照UTF-8的對(duì)照表找不到對(duì)應(yīng)的字符钧舌,所以就無法顯示。

這個(gè)時(shí)候再將這個(gè)字符流轉(zhuǎn)成字節(jié)流試試呢

        String test = new String(bytes);
        byte[] bytes1 = test.getBytes();

debug一下看看結(jié)果涎跨,會(huì)發(fā)現(xiàn)byte1的數(shù)組長(zhǎng)度不再是2了洼冻,而是4位


image.png

可以發(fā)現(xiàn)最后一位仍然是88,但是前三位是由130代表的字符流轉(zhuǎn)變而來的隅很,原因則也是因?yàn)?30代表的Unicode字符大小落在了上面總結(jié)的編碼表的第三區(qū)間撞牢,所以需要三個(gè)字節(jié)來存儲(chǔ),于是乎變成了三位叔营,感興趣的可以將這三個(gè)字節(jié)轉(zhuǎn)成二進(jìn)制看看是什么普泡。

最后是結(jié)論

有了上面的一個(gè)小實(shí)踐,這里明白了兩個(gè)問題

  • 我在最開始那個(gè)流轉(zhuǎn)成字符串审编,再由字符串轉(zhuǎn)成流的問題中遇見了一種情況撼班,我使用ASCII碼編碼的時(shí)候,圖片無法顯示出來垒酬,但是字節(jié)數(shù)轉(zhuǎn)化前后是一樣的砰嘁;但是當(dāng)我使用ISO_8859_1編碼格式的時(shí)候,就可以顯示出圖片了勘究。于是可以得到的結(jié)論是這個(gè)圖片流里面的字符肯定都是那0-256范圍中的字符矮湘,由于ASCII碼表無法表示128-256,所以才會(huì)有字節(jié)數(shù)一樣口糕,但是結(jié)果不一樣的情況缅阳。
  • 當(dāng)我用UTF-8轉(zhuǎn)變的時(shí)候,發(fā)現(xiàn)轉(zhuǎn)換后的字節(jié)數(shù)組會(huì)比一開始的多很多景描,這可能是因?yàn)閳D片流中存在UTF-8無法識(shí)別的字符十办,比如,舉個(gè)極端的例子超棺,所有的8位二進(jìn)制全部是10xxxxxx形式的向族,對(duì)應(yīng)上面的UTF-8與Unicode轉(zhuǎn)碼表,就會(huì)發(fā)現(xiàn)無法對(duì)應(yīng)棠绘,自然是得不到想要的結(jié)果的

再次象征性總結(jié)

這次碼的也挺亂的件相,其實(shí)對(duì)Java中字節(jié)流與字符流的了解確實(shí)不夠透徹,也源于對(duì)計(jì)算機(jī)原理的基礎(chǔ)了解還不足氧苍,這次也算是個(gè)引子夜矗,后面還是要再多研究強(qiáng)化一下這方面的知識(shí)。

還有让虐,這里參考了很多阮一峰大神的一篇關(guān)于編解碼的文章紊撕,大神就是大神,講的就是通俗易懂澄干,雖然是十幾年前的文章逛揩,但是仍然有很強(qiáng)的可讀性

長(zhǎng)路漫漫~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末柠傍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辩稽,更是在濱河造成了極大的恐慌惧笛,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逞泄,死亡現(xiàn)場(chǎng)離奇詭異患整,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)喷众,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門各谚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人到千,你說我怎么就攤上這事昌渤。” “怎么了憔四?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵膀息,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我了赵,道長(zhǎng)潜支,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任柿汛,我火速辦了婚禮冗酿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘络断。我一直安慰自己裁替,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布妓羊。 她就那樣靜靜地躺著胯究,像睡著了一般。 火紅的嫁衣襯著肌膚如雪躁绸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天臣嚣,我揣著相機(jī)與錄音净刮,去河邊找鬼。 笑死硅则,一個(gè)胖子當(dāng)著我的面吹牛淹父,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怎虫,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼暑认,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼困介!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蘸际,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤座哩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后粮彤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體根穷,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年导坟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屿良。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惫周,死狀恐怖尘惧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情递递,我是刑警寧澤褥伴,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站漾狼,受9級(jí)特大地震影響重慢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逊躁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一似踱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稽煤,春花似錦核芽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匾二,卻和暖如春哮独,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背察藐。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工皮璧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人分飞。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓悴务,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親譬猫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子讯檐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 字符是用戶可以讀寫的最小單位别洪。計(jì)算機(jī)所能支持的字符組成的集合叨恨,就叫做字符集。字符集通常以二維表的形式存在蕉拢。二維表的...
    劉惜有閱讀 8,131評(píng)論 2 14
  • UTF-8 編碼提供了一種簡(jiǎn)便而向后兼容的方法, 使得那種完全圍繞 ASCII 設(shè)計(jì)的操作系統(tǒng), 比如 Unix,...
    謝大見閱讀 4,738評(píng)論 0 3
  • 編碼問題一直困擾著開發(fā)人員特碳,尤其在 Java 中更加明顯,因?yàn)?Java 是跨平臺(tái)語言晕换,不同平臺(tái)之間編碼之間的切換...
    x360閱讀 2,483評(píng)論 1 20
  • 2014-03-17 18:04 3月16日午乓,有機(jī)會(huì)在市工人文化宮聆聽了64歲的中國(guó)籍猶太后裔、上海市僑聯(lián)副主席沙...
    湘江隱士閱讀 908評(píng)論 0 0
  • 回顧了一下《正念的奇跡》共讀內(nèi)容闸准,正念強(qiáng)調(diào)當(dāng)你看到一棵樹益愈、一朵花時(shí),不要一眼萬年夷家、思緒紛飛蒸其,要能看到樹是樹,花是花...
    caucxiaom閱讀 381評(píng)論 0 0