最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于GBK與UTF-8互轉(zhuǎn)亂碼問題解讀

 更新時(shí)間:2025年02月24日 09:35:01   作者:為BUG而來  
GBK與UTF-8互轉(zhuǎn)亂碼問題,是因?yàn)榫幋a和解碼方式不一致導(dǎo)致的,UTF-8編碼的字符在GBK中解碼可能會(huì)出現(xiàn)亂碼,而GBK編碼的字符在UTF-8中解碼則通??梢赃€原,ISO-8859-1編碼是單字節(jié)編碼,可以保證亂碼字符串的還原

GBK與UTF-8互轉(zhuǎn)亂碼問題

我們知道在計(jì)算機(jī)內(nèi)存中,存儲(chǔ)的是二進(jìn)制數(shù)據(jù),在網(wǎng)絡(luò)傳輸中,也是二進(jìn)制數(shù)據(jù),但最終呈現(xiàn)給用戶的是字符串,二進(jìn)制與字符串的轉(zhuǎn)化就需要編碼、解碼的參與,如果世界上只有一種字符編碼方式,就不會(huì)有亂碼這一說了,但事實(shí)是,編碼的方式太多了,utf-8、utf-32、utf-16、gbk、gb2312、iso-8859-1、big5、unicode等等。由于每個(gè)編碼的規(guī)則不一樣,一般都不能用一種進(jìn)行編碼,用另一種進(jìn)行解碼。

utf-8中,一個(gè)字母用一個(gè)字節(jié)表示,一個(gè)漢字用三個(gè)字節(jié)表示,特殊的漢字用四個(gè)字節(jié)表示,而gbk中,一個(gè)字母用一個(gè)字節(jié)表示,一個(gè)漢字用兩個(gè)字節(jié)表示

有一個(gè)說法,內(nèi)存中存儲(chǔ)的二進(jìn)制是unicode碼,如果內(nèi)存中的數(shù)據(jù)需要存儲(chǔ)或傳輸時(shí),才會(huì)進(jìn)行一次轉(zhuǎn)化,將unicode碼轉(zhuǎn)化成其它的編碼二進(jìn)制(有待考證)。個(gè)人覺得這種方式很合理,畢竟unicode碼中每個(gè)字符都有獨(dú)一無二的二進(jìn)制與之對(duì)應(yīng)。

排查亂碼問題,難度在于是在哪個(gè)環(huán)節(jié)出了問題,但亂碼的本質(zhì)都是一樣的,讀取二進(jìn)制的編碼和最初將字符串轉(zhuǎn)化成二進(jìn)制的編碼方式不一致

此處說明一個(gè)概念,編碼指將字符串轉(zhuǎn)化成二進(jìn)制,解碼指將二進(jìn)制轉(zhuǎn)化成字符串

UTF-8編碼,GBK解碼

在這我們討論一下,gbk和utf-8互轉(zhuǎn)的亂碼問題,直接上代碼:

package com.hskw.test;

import java.io.UnsupportedEncodingException;
 
public class CodingTest {
	public static void main(String[] args) throws UnsupportedEncodingException {
		String str = "你好,世界";
		System.out.println("字符串長度:"+str.length());
		
		byte[] utfBytes = str.getBytes("utf-8");
		System.out.println("utf-8需要"+utfBytes.length+"字節(jié)存儲(chǔ)");
		
		byte[] gbkBytes = str.getBytes("gbk");
		System.out.println("gbk需要"+gbkBytes.length+"字節(jié)存儲(chǔ)");
	}
}

以上代碼運(yùn)行打印出以下內(nèi)容:

字符串長度:5

utf-8需要15字節(jié)存儲(chǔ)

gbk需要10字節(jié)存儲(chǔ)

可以看出,utf-8存儲(chǔ)一個(gè)漢字,需要3個(gè)字節(jié),gbk存儲(chǔ)一個(gè)漢字,需要2個(gè)字節(jié)。

現(xiàn)用單個(gè)字符測試。

package com.hskw.test;

import java.io.UnsupportedEncodingException;
 
public class CodingTest {
	public static void main(String[] args) throws UnsupportedEncodingException {
		String str = "你";
		
		byte[] utfBytes = str.getBytes("utf-8");
		for(byte utfByte:utfBytes){
			//字節(jié)對(duì)應(yīng)的十進(jìn)制是負(fù)數(shù),因java中的二進(jìn)制使用補(bǔ)碼表示的,此處使用0xff 還原成int表示的數(shù)據(jù),再轉(zhuǎn)化成16進(jìn)制
			System.out.print(Integer.toHexString((utfByte & 0xFF)) +",");
		}
		System.out.println();
		String utf2gbkStr = new String(str.getBytes("utf-8"),"gbk");
		System.out.println("utf-8轉(zhuǎn)化成gbk:"+utf2gbkStr);
		
		byte[] gbkBytes = utf2gbkStr.getBytes("gbk");
		for(byte gbkByte:gbkBytes){
			System.out.print(Integer.toHexString((gbkByte & 0xFF))+",");
		}
		
		System.out.println();
		String gbk2utfStr = new String(utf2gbkStr.getBytes("gbk"),"utf-8");
		System.out.println("gbk轉(zhuǎn)化成utf-8:"+gbk2utfStr);
	}
}

運(yùn)行上面代碼,得出的結(jié)果:

e4,bd,a0,

utf-8轉(zhuǎn)化成gbk:浣?

e4,bd,3f,

gbk轉(zhuǎn)化成utf-8:??

用兩個(gè)字符測試,將上述代碼String str = “你”改成String str = “你好”。運(yùn)行代碼,得出的結(jié)果:

e4,bd,a0,e5,a5,bd,

utf-8轉(zhuǎn)化成gbk:浣犲ソ

e4,bd,a0,e5,a5,bd,

gbk轉(zhuǎn)化成utf-8:你好

上述實(shí)驗(yàn)中,utf-8轉(zhuǎn)化成gbk出現(xiàn)亂碼,這個(gè)很好理解,但是再還原回去,gbk轉(zhuǎn)化成utf-8,單個(gè)中文字符依然是亂碼,兩個(gè)字符卻能正常顯示,這個(gè)到底是怎么回事呢?

經(jīng)過一番研究,想把這個(gè)事說明白,還需要從它們的編碼規(guī)則著手。

ISO-8859-1

單字節(jié)編碼,向下兼容ASCII,其編碼范圍是0x00-0xFF,0x00-0x7F之間完全和ASCII一致,0x80-0x9F之間是控制字符,0xA0-0xFF之間是文字符號(hào)。

GBK

采用單雙字節(jié)變長編碼,英文使用單字節(jié)編碼,完全兼容ASCII字符編碼,中文部分采用雙字節(jié)編碼。雙字節(jié)其編碼范圍從8140至FEFE(剔除xx7F)。

  • 單字節(jié):00000000 - 01111111
  • 雙字節(jié):10000001 01000000 - 11111110 11111110 (剔除******** 01111111)

單字節(jié)、雙字節(jié)的區(qū)分通過高字節(jié)高位區(qū)分,單字節(jié)高位為0,雙字節(jié)的高字節(jié)高位為1。

UTF-8

可變長字符編碼,是unicode碼的具體實(shí)現(xiàn),UTF-8用1到6個(gè)字節(jié)編碼Unicode字符。

UTF-8編碼規(guī)則:如果只有一個(gè)字節(jié)則其最高二進(jìn)制位為0;如果是多字節(jié),其第一個(gè)字節(jié)從最高位開始,連續(xù)的二進(jìn)制位值為1的個(gè)數(shù)決定了其編碼的字節(jié)數(shù),其余各字節(jié)均以10開頭。

  • 1字節(jié) 0xxxxxxx
  • 2字節(jié) 110xxxxx 10xxxxxx
  • 3字節(jié) 1110xxxx 10xxxxxx 10xxxxxx
  • 4字節(jié) 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  • 5字節(jié) 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  • 6字節(jié) 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

明白上述GBK和UTF-8的編碼規(guī)則,我們再分析一下,單個(gè)中文字符是亂碼,兩個(gè)字符卻能正常顯示的問題。

“你”

UTF-8編碼對(duì)應(yīng)的二進(jìn)制:11100100 10111101 10100000

將上述二進(jìn)制通過GBK進(jìn)行解碼,根據(jù)GBK規(guī)則,第一個(gè)字節(jié)高位為1,使用雙字節(jié)編碼,

“11100100 10111101”解碼成“浣”,“10100000”對(duì)于GBK來說是非法的,就解碼成了一種特殊字符“?”。

看看能不能將“浣?”還原回“你”呢?

GBK編碼對(duì)應(yīng)的二進(jìn)制:11100100 10111101 00111111

看到上述的二進(jìn)制,根本不符合UTF-8編碼規(guī)則,故用UTF-8進(jìn)行解碼,是解碼成了一些特殊字符“??”。

對(duì)于上述情況可以看出,一個(gè)二進(jìn)制,如果不符合當(dāng)前的編碼規(guī)則,會(huì)被解碼成特殊字符,但此特殊字符再進(jìn)行編碼,是回不到最初的二進(jìn)制的。

用同樣的方式,分析“你好”為什么最終可以正常顯示。

UTF-8編碼對(duì)應(yīng)的二進(jìn)制:11100100 10111101 10100000 11100101 10100101 10111101

將上述二進(jìn)制通過GBK進(jìn)行編碼,根據(jù)GBK規(guī)則,使用雙字節(jié)編碼,“1100100 10111101”解碼成“浣”,“10100000 11100101”解碼成“犲”,“10100101 10111101”解碼成“ソ”。

看看能不能將“浣犲ソ”還原成“你好”呢?

GBK 編碼對(duì)應(yīng)的二進(jìn)制:11100100 10111101 10100000 11100101 10100101 10111101

可以看出二進(jìn)制是可以被還原的,將此二進(jìn)制通過UTF-8解碼,肯定能變成“你好”。

一個(gè)字符串,通過UTF-8進(jìn)行編碼,再通過GBK進(jìn)行解碼,再將得到的字符串進(jìn)行GBK編碼,最后將得到的二進(jìn)制通過UTF-8解碼,能否還原到最初的字符串,在于UTF-8編碼后得到的二進(jìn)制,是否符合GBK的編碼規(guī)則,如果符合,最終就可以還原,如果不符合,就不可還原。

GBK編碼,UTF-8解碼

package com.hskw.test;

import java.io.UnsupportedEncodingException;
 
public class CodingTest {
	public static void main(String[] args) throws UnsupportedEncodingException {
		String str = "你好";
		
		byte[] gbkBytes = str.getBytes("gbk");
		for(byte gbkByte:gbkBytes){
			//字節(jié)對(duì)應(yīng)的十進(jìn)制是負(fù)數(shù),因java中的二進(jìn)制使用補(bǔ)碼表示的,此處使用0xff 還原成int表示的數(shù)據(jù),再轉(zhuǎn)化成16進(jìn)制
			System.out.print(Integer.toHexString((gbkByte & 0xFF)) +",");
		}
		System.out.println();
		String gbk2utfStr = new String(str.getBytes("gbk"),"utf-8");
		System.out.println("gbk轉(zhuǎn)化成utf-8:"+gbk2utfStr);
		
		byte[] utfBytes = gbk2utfStr.getBytes("utf-8");
		for(byte utfByte:utfBytes){
			System.out.print(Integer.toHexString((utfByte & 0xFF))+",");
		}
		
		System.out.println();
		String utf2gbkStr = new String(gbk2utfStr.getBytes("utf-8"),"gbk");
		System.out.println("utf-8轉(zhuǎn)化成gbk:"+utf2gbkStr);
	}
}

運(yùn)行上述代碼,結(jié)果為:

c4,e3,ba,c3,

gbk轉(zhuǎn)化成utf-8:???

ef,bf,bd,ef,bf,bd,ef,bf,bd,

utf-8轉(zhuǎn)化成gbk:錕斤拷錕?

上述結(jié)果應(yīng)該都在意料之中,我們通過上述的方法分析一下。

“你好”GBK編碼的二進(jìn)制:11000100 11100011 10111010 11000011

GBK編碼的二進(jìn)制數(shù)據(jù),完全匹配不了UTF-8的編碼規(guī)則,最終UTF-8只能按如下方式匹配,查看第一個(gè)字節(jié),開頭“110”,理論上匹配兩個(gè)字節(jié),但看下一個(gè)字節(jié),開頭卻不是“10”,最終“11000100”解碼成“?”,看第二個(gè)字節(jié)開頭是“1110”,理論匹配三個(gè)字節(jié),看下個(gè)字節(jié)符合,以“10”開頭,但下下個(gè)字節(jié)開頭是“110”,不符合匹配,最終“11100011 10111010”解碼成“?”,同理“11000011”也解碼成“?”,這個(gè)符號(hào)都是為找不到對(duì)應(yīng)規(guī)則隨意匹配的一個(gè)特殊字符。

“???”UTF-8編碼的二進(jìn)制為:11101111 10111111 10111101 11101111 10111111 10111101 11101111 10111111 10111101

這個(gè)二進(jìn)制和原先的二進(jìn)制不相同,根本轉(zhuǎn)化不到最初的字符串,按照GBK的編碼規(guī)則,“11101111 10111111”編碼成“錕”,“10111101 11101111” 編碼成“斤”,“10111111 10111101”編碼成“拷”,“11101111 10111111”編碼成“錕”,“10111101”不符合GBK規(guī)則,編碼成特殊字符“?”。

理論上說,用GBK編碼,UTF-8解碼的字符串是不能還原到最初的字符串的,因UTF-8編碼規(guī)則的特殊性,GBK編出的二進(jìn)制,是很難匹配上的。

總結(jié)

理論上說,系統(tǒng)出現(xiàn)亂碼,將亂碼還原到最初的樣子,上述UTF-8編碼,GBK解碼,這個(gè)有時(shí)是可以還原的,有時(shí)是還原不了的,要看UTF-8編碼的二進(jìn)制是否都能符合GBK的編碼規(guī)則,但GBK編碼,UTF-8解碼,這個(gè)基本是條不歸路。

但實(shí)際中,有一種情況,是100%可以將亂碼還原成最初的字符串。就是任意編碼格式編碼,ISO-8859-1解碼,這個(gè)主要因?yàn)镮SO-8859-1是單字節(jié)編碼,而且匹配所有單字節(jié)情況,亂碼字符串總是可以還原到最初的二進(jìn)制。

拓展一個(gè)小知識(shí)點(diǎn):

關(guān)于進(jìn)制的表示有兩種方式,一種是前綴表示法,一種是后綴表示法。

前綴表示法

  • 十六進(jìn)制:0x
  • 十進(jìn)制:無前綴
  • 八進(jìn)制:0
  • 二進(jìn)制:沒有表示符號(hào)

后綴表示法

  • B :二進(jìn)制數(shù)
  • Q :八進(jìn)制數(shù)
  • D :十進(jìn)制數(shù)
  • H :十六進(jìn)制數(shù)

對(duì)于十進(jìn)制數(shù)通常不加后綴,也即十進(jìn)制數(shù)后的字母 D 可省略。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 網(wǎng)址URL的大小寫敏感嗎,有什么區(qū)別?

    網(wǎng)址URL的大小寫敏感嗎,有什么區(qū)別?

    在?Web?開發(fā)中,網(wǎng)址URL?的大小寫問題可能引發(fā)意想不到的行為,本文將從技術(shù)規(guī)范、服務(wù)器配置、瀏覽器行為等多個(gè)維度,系統(tǒng)解析?URL?各組件的大小寫敏感性,并提供實(shí)踐建議,可有效避免因?URL?大小寫不一致引發(fā)的搜索引擎優(yōu)化問題、鏈接失效及用戶體驗(yàn)下降
    2025-04-04
  • DeepSeek R1本地化部署 Ollama + Chatbox 如何打造最強(qiáng) AI 工具

    DeepSeek R1本地化部署 Ollama + Chatbox 如

    文章介紹了如何在本地部署DeepSeekR1模型并使用Chatbox進(jìn)行交互,使用戶能夠擁有強(qiáng)大的AI工具,感興趣的朋友跟隨小編一起看看吧
    2025-02-02
  • 全民學(xué)編程之 Hello World

    全民學(xué)編程之 Hello World

    Hello World的迷人之處在于:它是簡單的,即便沒有任何基礎(chǔ),你也可以輕松理解,但它又不是蒼白的,它帶有一個(gè)程序語言鮮明的特色,體現(xiàn)了該語言最基本的思想和特征。同樣也是Hello World,讓人體會(huì)到第一份成功的喜悅
    2014-01-01
  • VSCode SSH遠(yuǎn)程連接與刪除的實(shí)現(xiàn)步驟

    VSCode SSH遠(yuǎn)程連接與刪除的實(shí)現(xiàn)步驟

    本文主要介紹了VSCode SSH遠(yuǎn)程連接與刪除的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • 動(dòng)態(tài)語言、動(dòng)態(tài)類型語言、靜態(tài)類型語言、強(qiáng)類型語言、弱類型語言介紹

    動(dòng)態(tài)語言、動(dòng)態(tài)類型語言、靜態(tài)類型語言、強(qiáng)類型語言、弱類型語言介紹

    這篇文章主要介紹了動(dòng)態(tài)語言、動(dòng)態(tài)類型語言、靜態(tài)類型語言、強(qiáng)類型語言、弱類型語言介紹,需要的朋友可以參考下
    2015-04-04
  • 趣談Unicode、Ascii、utf-8、GB2312、GBK等編碼知識(shí)

    趣談Unicode、Ascii、utf-8、GB2312、GBK等編碼知識(shí)

    這篇文章主要介紹了趣談Unicode、Ascii、utf-8、GB2312、GBK等編碼知識(shí),文章寫的詼諧風(fēng)趣,膜拜啊,需要的朋友可以參考下
    2014-07-07
  • 詳解MD5算法的原理以及C#和JS的實(shí)現(xiàn)

    詳解MD5算法的原理以及C#和JS的實(shí)現(xiàn)

    MD5?是哈希算法(散列算法)的一種應(yīng)用。這篇文章主要和大家介紹一下MD5算法的原理以及C#和JS的實(shí)現(xiàn),文中的示例代碼講解詳細(xì),需要的可以參考一下
    2023-03-03
  • 2019-nCoV 全國新型肺炎疫情每日動(dòng)態(tài)趨勢可視圖

    2019-nCoV 全國新型肺炎疫情每日動(dòng)態(tài)趨勢可視圖

    大家被新型冠狀病毒搞的人心慌慌,每天宅在家里那也去不了,今天小編給大家分享2019-nCoV 全國新型肺炎疫情每日動(dòng)態(tài)趨勢可視圖,需要的朋友可以參考下
    2020-02-02
  • 可能是最通俗的一篇介紹markdown的文章

    可能是最通俗的一篇介紹markdown的文章

    這些日子一直在簡書上使用markdown寫作,已經(jīng)漸漸的癡迷于這種簡潔純粹的寫作方式了。不過就我逐漸入門markdown的寫作過程來看,目前我看到的各種介紹markdown寫作方式的文章都還略顯極客,對(duì)于大多數(shù)像我一樣沒有基礎(chǔ)的普通人來說,可能內(nèi)容上的可接受性沒有那么強(qiáng)
    2016-08-08
  • 萬萬沒想到Chrome的歷史記錄竟然可以這么玩

    萬萬沒想到Chrome的歷史記錄竟然可以這么玩

    最近遇到一個(gè)棘手的問題,需要查找含有某個(gè)關(guān)鍵字的網(wǎng)頁,但是通過chrome原生的歷史記錄查出來的,查到的結(jié)果并不滿意,今天小編就給大家分享一篇教程幫助大家解決Chrome歷史記錄的問題,感興趣的朋友一起看看吧
    2020-10-10

最新評(píng)論

长武县| 会泽县| 茶陵县| 通许县| 平昌县| 西安市| 观塘区| 芷江| 中西区| 拉孜县| 钟祥市| 乌拉特中旗| 新民市| 大石桥市| 祁连县| 大邑县| 克东县| 嵊泗县| 嘉鱼县| 黔西县| 民勤县| 南宫市| 马关县| 铁岭市| 天峨县| 新沂市| 轮台县| 平顶山市| 连江县| 丹棱县| 奇台县| 青阳县| 迭部县| 临潭县| 盐山县| 克东县| 饶河县| 通榆县| 望城县| 浦江县| 杭锦后旗|