別再混淆了,編程中常用的字符編碼知識點
來源:網(wǎng)路素材

字符集和字符編碼
但js代碼中的字符串類型是UTF-16編碼的,這也是為什么會碰到api接口返回字符串在前端出現(xiàn)亂碼,因為多數(shù)服務都使用utf-8編碼,前后編碼方式不一致。
ASCII
其中,每一個二進制位(bit)有 0和1 兩種狀態(tài)。一個字節(jié)(byte)則有8個二進制位,可以有256種狀態(tài)。
'a'.charCodeAt() // 97
'A'.charCodeAt() // 65
'9'.charCodeAt() // 57
'.'.charCodeAt() // 46
65 ^ 32 = 97
// A ^ 32 = a
字符集的發(fā)展歷史
Unicode
碼點
Unicode 規(guī)定了每個字符的數(shù)字編號,這個編號被稱為 碼點(code point)。碼點以 U+hex 的形式表示,U+是代表Unicode的前綴,而 hex 是一個16進制數(shù)。取值范圍是從 U+0000 到 U+10FFFF。
字符平面
目前的Unicode分成了17個編組,也稱平面,每個平面有65536個碼點。第一個平面是基本多語言平面,范圍:U+0000 - U+FFFF,多數(shù)常見字符都在該區(qū)間。其他平面則為輔助平面,范圍:U+10000 到 U+10FFFF,如我們在網(wǎng)上常見 Emoji 表情。碼元
碼元(Code Unit)可以理解為對碼點進行編碼時的最小基本單元,碼元是一個整體。而字符編碼的作用就是將Unicode碼點轉(zhuǎn)換成碼元序列。Unicode常用的編碼方式有 UTF-8 、UTF-16 和 UTF-32,UTF是Unicode TransferFormat的縮寫。UTF-8是8位的單字節(jié)碼元,UTF-16是16位的雙字節(jié)碼元,UTF-32是32位的四字節(jié)碼元。
編碼方式 | 碼元 | 編碼后字節(jié)數(shù) |
UTF-8 | 8位 | 1-4字節(jié) |
UTF-16 | 16位 | 2字節(jié)或者4字節(jié) |
UTF-32 | 32位 | 4字節(jié) |
UTF-8
1個字節(jié)的字符,第一位為0,后7位為碼點,與ASCII相同。
n個字節(jié)的字符,第一個字節(jié)前面 n 位都是1,n+1位是0,可據(jù)此判斷有幾個字節(jié)。后面的幾個字節(jié)都是?10?為開頭2位。
這里規(guī)定的都是前綴,對于字符的碼點,需要進行截取后依次放入除前綴外的其他位,所以UTF-8又被稱為前綴碼。格式如表:
字節(jié)數(shù) | 碼點位數(shù) | 碼點范圍 | 編碼方式 |
1 | 7 | U+0000~U+007F | 0××××××× |
2 | 11 | U+0080~U+07FF | 110××××× 10×××××× |
3 | 16 | U+0800~U+FFFF | 1110×××× 10×××××× 10×××××× |
4 | 21 | U+10000~U+10FFFF | 11110××× 10×××××× 10×××××× 10×××××× |
'好'的Unicode碼點:'好'.codePointAt() \\ 22909,結(jié)果是22909;
22909在UTF-8的3字節(jié)數(shù)的編碼區(qū)間 U+0800 (2048) ~ U+FFFF (65535);
22909的二進制值:101100101111101,有15位;
而3字節(jié)數(shù)的編碼需要16位,前面補0,根據(jù)表中規(guī)則分成3組:0101 100101 111101;
依次填入對應的前綴:11100101 10100101 10111101,得到3個字節(jié);
將得到的三個字節(jié)轉(zhuǎn)成十六進制數(shù)據(jù):E5 A5 BD,所以漢字 '好' 的UTF-8就是:E5 A5 BD。
encodeURI('好') // '%E5%A5%BD'
UTF-16
碼點小于U+FFFF,基本字符,不需處理,直接使用,占兩個字節(jié)。
否則,拆分成兩個碼元,四個字節(jié),cp表示碼點:低位——((cp - 65536) / 1024) + 0xD800,值范圍是 0xD800~0xDBFF;高位——((cp - 65536) % 1024) + 0xDC00,值范圍是 0xDC00~0xDFFF。
漢字 '好','好'.codePointAt() // 22909,碼點小于U+FFFF,直接進行十六進制轉(zhuǎn)換:579D。表情符號 '',''.codePointAt() // 128516,碼點需要拆分:低位:Math.floor(((128516 - 65536) / 1024)) + 0xD800 // 55357, 得到 D83D高位:((128516 - 65536) % 1024) + 0xDC00 // 56836,得到 DE04
String.fromCharCode(0xD83D, 0xDE04) // ''
UTF-32是定長的編碼,每個碼位使用四個字節(jié)進行編碼。優(yōu)點是和unicode一一對應,缺點是太浪費空間。
比較
// UTF-8
'a': 97 - 0x61
'好': 22909 - (0xE5 0xA5 0xBD)
'': 128516 - (0xF0 0x9F 0x98 0x84)
// UTF-16
'a': 97 - 0x0061
'好': 22909 - 0x597d
'': 128516 - (0xD83D, 0xDE04)
前端開發(fā)中的編碼
字符串長度計算
ASCII碼和大部分中文,都是一個碼元
而表情字符和其他特殊字符都是兩個碼元
'a'.length // 1
'好'.length // 1,多數(shù)漢字都是基本字符平面,只有一個碼元,長度就為1。
''.length // 2
組合字符的長度
'e?'.length // 2
'e\u0301'.length // 2
// 獲取碼點時,忽略了標點符號,顯示的是字母的碼點
'e?'.codePointAt() // 101
'e'.codePointAt() // 101
'e?'.normalize().length = 1。
多碼元字符操作
''[0] // '\uD83D'
''[1] // '\uDE04'
'123'[0] // '1'
let smile = ''
for(let i = 0; i < smile.length; i++) {
console.log(smile[i])
}
// ?
// ?
for (let tt of smile) {
console.log(tt)
}
//
[...''][0] // ''
Array.from('') // ['']
String.fromCodePoint(''.codePointAt()) // ''
''.slice(0, 2) // ''
''.slice(0, 1) // '\uD83D'
''.slice(1, 2) // '\uDE04'
''.substr(0,1) // '\uD83D'
''.substr(0,2) // ''
''.split('') // ['\uD83D', '\uDE04']
正則中的 u 修飾符
/^\S$/.test('') // false
/^\S$/u.test('') // true
/^\S$/u.test('e?') // false
/^\S$/u.test('e\u0301') // false
轉(zhuǎn)義字符
'\x3f'.length // 1
'?'.length // 1
'\x3f'[0] // '?'
'\x3f'.split('') // ['?']
常用API
處理碼點和字符
charAt(index)
從一個字符串中返回指定的字符,對于多碼元字符,仍會返回碼元字符:
'a'.charAt() // 'a'
''.charAt() // '\uD83D'
''.charAt(1) // '\uDE04'
charCodeAt(index)
返回0到65535之間的整數(shù)碼點值。對于多碼元如果字符的碼點大于U+FFFF,則返回第一個碼元值,還可以加索引參數(shù)取后面碼元的值。codePointAt(pos)
返回Unicode碼點,多碼元也能返回完整的碼點值。codePointAt可以傳入索引參數(shù),對多碼元字符取第二個碼元值。
// 小于 U+FFFF
'好'.codePointAt() // 22909
'好'.charCodeAt() // 22909
// 大于 U+FFFF
''.charCodeAt() // 55357
''.charCodeAt(1) // 56836
''.codePointAt() // 128516
''.codePointAt(1) // 56836
String.fromCharCode(num1[, ...[, numN]])
返回由指定的UTF-16碼點序列創(chuàng)建的字符串。參數(shù)范圍0到65535,大于65535的數(shù)據(jù)將被截斷,結(jié)果不準確。對于多碼元字符,則會將兩個碼元組合得到該字符。String.fromCodePoint(num1[, ...[, numN]])
返回使用指定的代碼點序列創(chuàng)建的字符串。可以處理多碼元字符的完整碼點值。
String.fromCharCode(55357, 56836, 123) // '{'
String.fromCodePoint(128516, 123, 8776) // '{≈'
TextEncoder
const txtEn = new TextEncoder()
const enVal = txtEn.encode('好')
// Uint8Array(3) [229, 165, 189]
const txtDe = new TextDecoder()
txtDe.decode(enVal) // '好'
String.prototype.normalize()
'é' === 'e?' // false
'é' === 'e?'.normalize() // true
URL的UTF8編解碼
encodeURI() 和 encodeURIComponent()
decodeURI() 和 decodeURIComponent()
encodeURI('好') // '%E5%A5%BD'
decodeURI('%E5%A5%BD') // '好'
encodeURIComponent('好') // '%E5%A5%BD'
decodeURIComponent('%E5%A5%BD') // '好'
encodeURI('hello') // 'hello'
encodeURIComponent('hello') // 'hello'
encodeURIComponent('') // '%F0%9F%98%84'
encodeURI和encodeURIComponent的區(qū)別
URL元字符:分號(;),逗號(’,’),斜杠(/),問號(?),冒號(:),at(@),&,等號(=),加號(+),美元符號($),井號(#)。
encodeURIComponent(';,/@&=') // '%3B%2C%2F%40%26%3D'
encodeURI(';,/@&=') // ';,/@&='版權聲明:本文來源網(wǎng)絡,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯(lián)系我進行刪除。
???????????????? ?END ????????????????
關注我的微信公眾號,回復“加群”按規(guī)則加入技術交流群。
點擊“閱讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。
