通過(guò)javascript進(jìn)行UTF-8編碼的實(shí)現(xiàn)方法
javascript的字符集:
javascript程序是使用Unicode字符集編寫的。Unicode是ASCII和Latin-1的超集,并支持地球上幾乎所有的語(yǔ)言。ECMAScript3要求JavaScript必須支持Unicode2.1及后續(xù)版本,ECMAScript5則要求支持Unicode3及后續(xù)版本。所以,我們編寫出來(lái)的
javascript程序,都是使用Unicode編碼的。
UTF-8
UTF-8(UTF8-bit Unicode Transformation Format)是一種針對(duì)Unicode的可變長(zhǎng)度字符編碼,也是一種前綴碼。
它可以用來(lái)表示Unicode標(biāo)準(zhǔn)中的任何字符,且其編碼中的第一個(gè)字節(jié)仍與ASCII兼容,這使得原來(lái)處理ASCII字符的軟件無(wú)須或只須做少部分修改,即可繼續(xù)使用。因此,它逐漸成為電子郵件、網(wǎng)頁(yè)及其他存儲(chǔ)或發(fā)送文字的應(yīng)用中,優(yōu)先采用的編碼。
目前大部分的網(wǎng)站,都是使用的UTF-8編碼。
將javascript生成的Unicode編碼字符串轉(zhuǎn)為UTF-8編碼的字符串
如標(biāo)題所說(shuō)的應(yīng)用場(chǎng)景十分常見(jiàn),例如發(fā)送一段二進(jìn)制到服務(wù)器時(shí),服務(wù)器規(guī)定該二進(jìn)制內(nèi)容的編碼必須為UTF-8。這種情況下,我們必須就要通過(guò)程序?qū)avascript的Unicode字符串轉(zhuǎn)為UTF-8編碼的字符串。
轉(zhuǎn)換方法
轉(zhuǎn)換之前我們必須了解Unicode的編碼結(jié)構(gòu)是固定的。
不信可以試一試 String 的 charCodeAt 這個(gè)方法,看看返回的 charCode 占幾個(gè)字節(jié)。
•英文占1個(gè)字符,漢字占2個(gè)字符
然而,UTF-8的編碼結(jié)構(gòu)長(zhǎng)度是根據(jù)某單個(gè)字符的大小來(lái)決定長(zhǎng)度有多少。
下面為單個(gè)字符的大小占用幾個(gè)字節(jié)。單個(gè)unicode字符編碼之后的最大長(zhǎng)度為6個(gè)字節(jié)。
•1個(gè)字節(jié):Unicode碼為0 - 127
•2個(gè)字節(jié):Unicode碼為128 - 2047
•3個(gè)字節(jié):Unicode碼為2048 - 0xFFFF
•4個(gè)字節(jié):Unicode碼為65536 - 0x1FFFFF
•5個(gè)字節(jié):Unicode碼為0x200000 - 0x3FFFFFF
•6個(gè)字節(jié):Unicode碼為0x4000000 - 0x7FFFFFFF
具體請(qǐng)看圖片:
因?yàn)橛⑽暮陀⑽淖址腢nicode碼為0 - 127,所以英文在Unicode和UTF-8中的長(zhǎng)度和字節(jié)都是一致的,只占用1個(gè)字節(jié)。這也就是為什么UTF8是Unicode的超集!
現(xiàn)在我們?cè)賮?lái)討論漢字,因?yàn)闈h字的unicode碼區(qū)間為0x2e80 - 0x9fff, 所以漢字在UTF8中的長(zhǎng)度最長(zhǎng)為3個(gè)字節(jié)。
那么漢字是如何從Unicode的2個(gè)字節(jié)轉(zhuǎn)換為UTF8的三個(gè)字節(jié)的哪?
假設(shè)我需要把漢字"中"轉(zhuǎn)為UTF-8的編碼
1、獲取漢字Unicode值大小
var str = '中'; var charCode = str.charCodeAt(0); console.log(charCode); // => 20013
2、根據(jù)大小判斷UTF8的長(zhǎng)度
由上一步我們得到漢字"中"的charCode為20013.然后我們發(fā)現(xiàn)20013位于2048 - 0xFFFF這個(gè)區(qū)間里,所以漢字"中"應(yīng)該在UTF8中占3個(gè)字節(jié)。
3、補(bǔ)碼
既然知道漢字"中"需要占3個(gè)字節(jié),那么這3個(gè)字節(jié)如何得到哪?
這就需要設(shè)計(jì)到補(bǔ)碼,具體補(bǔ)碼邏輯如下:
好吧,我知道這個(gè)圖你們也看不明白,還是我來(lái)講吧!
具體的補(bǔ)位碼如下,"x"表示空位,用來(lái)補(bǔ)位的。
•0xxxxxxx
•110xxxxx 10xxxxxx
•1110xxxx 10xxxxxx 10xxxxxx
•11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
•111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
•1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
warning:有沒(méi)有發(fā)現(xiàn)?補(bǔ)位碼第一個(gè)字節(jié)前面有幾個(gè)1就表示整個(gè)UTF-8編碼占多少個(gè)字節(jié)!UTF-8解碼為Unicode就是利用的這個(gè)特點(diǎn)哦~
我們先舉個(gè)簡(jiǎn)單的例子。把英文字母"A"轉(zhuǎn)為UTF8編碼。
1、“A”的charCode為65
2、65位于0-127的區(qū)間,所以“A”占一個(gè)字節(jié)
3、UTF8中一個(gè)字節(jié)的補(bǔ)位為0xxxxxxx,x表示的是空位,是用來(lái)補(bǔ)位的。
4、將65轉(zhuǎn)為二進(jìn)制得到1000001
5、將1000001按照從前到后的順序,依次補(bǔ)到1xxxxxxx的空位中,得到01000001
6、將11000001轉(zhuǎn)為字符串,得到"A"
7、最終,"A"為UTF8編碼之后“A”
通過(guò)這個(gè)小例子,我們是否再次驗(yàn)證了UTF-8是Unicode的超集!
好了,我們現(xiàn)在再回到漢字"中"上,之前我們已經(jīng)得到了"中"的charCode為20013,二進(jìn)制為01001110 00101101。具體如下:
var code = 20013; code.toString(2); // => 100111000101101 等同于 01001110 00101101
然后,我們按照上面“A”補(bǔ)位的方法,來(lái)給"中"補(bǔ)位。
將01001110 00101101按照從前到后的順序依此補(bǔ)位到1110xxxx 10xxxxxx 10xxxxxx上.得到11100100 10111000 10101101.
4、得到UTF8編碼的內(nèi)容
通過(guò)上面的步驟,我們得到了"中"的三個(gè)UTF8字節(jié),11100100 10111000 10101101。
我們將每個(gè)字節(jié)轉(zhuǎn)為16進(jìn)制,得到0xE4 0xB8 0xAD;
那么這個(gè)0xE4 0xB8 0xAD就是我們最終得到的UTF8編碼了。
我們使用nodejs的buffer來(lái)驗(yàn)證一下是否正確。
var buffer = new Buffer('中'); console.log(buffer.length); // => 3 console.log(buffer); // => <Buffer e4 b8 ad> // 最終得到三個(gè)字節(jié) 0xe4 0xb8 0xad
因?yàn)?6進(jìn)制是不分大小寫的,所以是不是跟我們算出來(lái)0xE4 0xB8 0xAD一模一樣。
將上面的編碼邏輯寫到一個(gè)函數(shù)中。
// 將字符串格式化為UTF8編碼的字節(jié) var writeUTF = function (str, isGetBytes) { var back = []; var byteSize = 0; for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); if (0x00 <= code && code <= 0x7f) { byteSize += 1; back.push(code); } else if (0x80 <= code && code <= 0x7ff) { byteSize += 2; back.push((192 | (31 & (code >> 6)))); back.push((128 | (63 & code))) } else if ((0x800 <= code && code <= 0xd7ff) || (0xe000 <= code && code <= 0xffff)) { byteSize += 3; back.push((224 | (15 & (code >> 12)))); back.push((128 | (63 & (code >> 6)))); back.push((128 | (63 & code))) } } for (i = 0; i < back.length; i++) { back[i] &= 0xff; } if (isGetBytes) { return back } if (byteSize <= 0xff) { return [0, byteSize].concat(back); } else { return [byteSize >> 8, byteSize & 0xff].concat(back); } } writeUTF('中'); // => [0, 3, 228, 184, 173] // 前兩位表示后面utf8字節(jié)的長(zhǎng)度。因?yàn)殚L(zhǎng)度為3,所以前兩個(gè)字節(jié)為`0,3` // 內(nèi)容為`228, 184, 173`轉(zhuǎn)成16進(jìn)制就是`0xE4 0xB8 0xAD`
// 讀取UTF8編碼的字節(jié),并專為Unicode的字符串 var readUTF = function (arr) { if (typeof arr === 'string') { return arr; } var UTF = '', _arr = this.init(arr); for (var i = 0; i < _arr.length; i++) { var one = _arr[i].toString(2), v = one.match(/^1+?(?=0)/); if (v && one.length == 8) { var bytesLength = v[0].length; var store = _arr[i].toString(2).slice(7 - bytesLength); for (var st = 1; st < bytesLength; st++) { store += _arr[st + i].toString(2).slice(2) } UTF += String.fromCharCode(parseInt(store, 2)); i += bytesLength - 1 } else { UTF += String.fromCharCode(_arr[i]) } } return UTF } readUTF([0, 3, 228, 184, 173]); => '中'
另外一種將中文解析得到UTF8字節(jié)碼的方法
另外一種比較簡(jiǎn)單的將中文轉(zhuǎn)為UTF8字節(jié)碼的方法比較簡(jiǎn)單,瀏覽器也提供了一個(gè)方法,而且這個(gè)方法大家都一直在用,是什么哪?就是encodeURI。當(dāng)然,encodeURIComponent也是可以的。
沒(méi)錯(cuò),就是這個(gè)方法。那么這個(gè)方法是怎么將一個(gè)Unicode編碼的中文轉(zhuǎn)為UTF8的字節(jié)碼嘞?
var str = '中'; var code = encodeURI(str); console.log(code); // => %E4%B8%AD
有沒(méi)有發(fā)現(xiàn)得到了一個(gè)轉(zhuǎn)義后的字符串,而且這個(gè)字符串中的內(nèi)容和我之前在上面得到的字節(jié)碼是一樣的~~~。
下面我們將%E4%B8%AD轉(zhuǎn)為一個(gè)number數(shù)組。
var codeList = code.split('%'); codeList = codeList.map(item => parseInt(item,16)); console.log(codeList); // => [228, 184, 173]
如此簡(jiǎn)單,有木有~~~
這個(gè)簡(jiǎn)便方法的原理是什么?
這里就涉及到的URI中的querystring編碼的問(wèn)題了。因?yàn)榘凑找?guī)定,URI中的querystring必須按照UTF8的編碼進(jìn)行傳輸,而JavaScript是Unicode的,所以瀏覽器就給我們提供了一個(gè)方法,也就是encodeURI/encodeURIComponent方法。這個(gè)方法會(huì)講
非英文字符(這里考慮下,為什么是非英文字符?)先轉(zhuǎn)為UTF8的字節(jié)碼,然后前面加個(gè)%進(jìn)行拼接,所以我們將漢字"中"轉(zhuǎn)義下便得到了"%E4%B8%AD".
好吧,原理就這些,沒(méi)有其他的了。
不過(guò),這種方法還有個(gè)缺點(diǎn),那就是只會(huì)轉(zhuǎn)義非英文字符,所以當(dāng)我們需要將英文字符也格式化為UTF8編碼時(shí),這個(gè)方法是達(dá)不到我們需求的,我們還需要額外的將英文字符也給轉(zhuǎn)義下。
那我想要解析回來(lái)應(yīng)該怎么做哪?用decodeURI/decodeURIComponent就可以了。
var codeList = [228, 184, 173]; var code = codeList.map(item => '%'+item.toString(16)).join(''); decodeURI(code); // => 中
好了,到這里本文也就介紹完UTF8的編碼了。
希望可以幫助大家了解到UTF-8編碼的原理。
以上就是小編為大家?guī)?lái)的通過(guò)javascript進(jìn)行UTF-8編碼的實(shí)現(xiàn)方法全部?jī)?nèi)容了,希望大家多多支持腳本之家~
- js 編碼轉(zhuǎn)換 gb2312 和 utf8 互轉(zhuǎn)的2種方法
- Javascript下的urlencode編碼解碼方法附decodeURIComponent
- js對(duì)圖片base64編碼字符串進(jìn)行解碼并輸出圖像示例
- js 顯示base64編碼的二進(jìn)制流網(wǎng)頁(yè)圖片
- utf-8編碼引起js輸出中文亂碼的解決辦法
- 將字符串轉(zhuǎn)換成gb2312或者utf-8編碼的參數(shù)(js版)
- JS 文字符串轉(zhuǎn)換unicode編碼函數(shù)
- JavaScript Base64編碼和解碼,實(shí)現(xiàn)URL參數(shù)傳遞。
- JavaScript實(shí)現(xiàn)Base64編碼轉(zhuǎn)換
- js下用gb2312編碼解碼實(shí)現(xiàn)方法
- JS實(shí)現(xiàn)的哈夫曼編碼示例【原始版與修改版】
相關(guān)文章
使用electron實(shí)現(xiàn)百度網(wǎng)盤懸浮窗口功能的示例代碼
這篇文章主要介紹了使用electron實(shí)現(xiàn)百度網(wǎng)盤懸浮窗口功能的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10關(guān)閉瀏覽器輸入框自動(dòng)補(bǔ)齊 兼容IE,FF,Chrome等主流瀏覽器
這篇文章主要介紹了關(guān)閉瀏覽器輸入框自動(dòng)補(bǔ)齊 兼容IE,FF,Chrome等主流瀏覽器,需要的朋友可以參考下。希望對(duì)大家有所幫助2014-02-02js代碼延遲一定時(shí)間后執(zhí)行一個(gè)函數(shù)的實(shí)例
下面小編就為大家?guī)?lái)一篇js代碼延遲一定時(shí)間后執(zhí)行一個(gè)函數(shù)的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02詳解JS截取字符串的三個(gè)方法substring,substr,slice
js中有三個(gè)截取字符的方法,分別是substring()、substr()、slice(),平時(shí)我們可能都用到過(guò),但總是會(huì)對(duì)這些方法有點(diǎn)混淆。本文將詳細(xì)介紹一下這三者的區(qū)別,需要的可以參考一下2022-03-03利用chrome瀏覽器進(jìn)行js調(diào)試并找出元素綁定的點(diǎn)擊事件詳解
“工欲善其事,必先利其器” 恩,這句話我覺(jué)得說(shuō)的特別有道理,下面這篇文章主要給大家介紹了關(guān)于利用chrome瀏覽器進(jìn)行js調(diào)試并找出元素綁定的點(diǎn)擊事件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-09-09JavaScript實(shí)現(xiàn)隨機(jī)點(diǎn)名的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何使用JavaScript實(shí)現(xiàn)隨機(jī)點(diǎn)名效果,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11