别再混淆了,编程中常用的字符编码知识点
共 7728字,需浏览 16分钟
·
2022-06-09 01:48
来源:网路素材
字符集和字符编码
但js代码中的字符串类型是UTF-16编码的,这也是为什么会碰到api接口返回字符串在前端出现乱码,因为多数服务都使用utf-8编码,前后编码方式不一致。
ASCII
其中,每一个二进制位(bit)有 0和1 两种状态。一个字节(byte)则有8个二进制位,可以有256种状态。
'a'.charCodeAt() // 97
'A'.charCodeAt() // 65
'9'.charCodeAt() // 57
'.'.charCodeAt() // 46
65 ^ 32 = 97
// A ^ 32 = a
字符集的发展历史
Unicode
码点
Unicode 规定了每个字符的数字编号,这个编号被称为 码点(code point)。码点以 U+hex 的形式表示,U+是代表Unicode的前缀,而 hex 是一个16进制数。取值范围是从 U+0000 到 U+10FFFF。
字符平面
目前的Unicode分成了17个编组,也称平面,每个平面有65536个码点。第一个平面是基本多语言平面,范围:U+0000 - U+FFFF,多数常见字符都在该区间。其他平面则为辅助平面,范围:U+10000 到 U+10FFFF,如我们在网上常见 Emoji 表情。码元
码元(Code Unit)可以理解为对码点进行编码时的最小基本单元,码元是一个整体。而字符编码的作用就是将Unicode码点转换成码元序列。Unicode常用的编码方式有 UTF-8 、UTF-16 和 UTF-32,UTF是Unicode TransferFormat的缩写。UTF-8是8位的单字节码元,UTF-16是16位的双字节码元,UTF-32是32位的四字节码元。
编码方式 | 码元 | 编码后字节数 |
UTF-8 | 8位 | 1-4字节 |
UTF-16 | 16位 | 2字节或者4字节 |
UTF-32 | 32位 | 4字节 |
UTF-8
1个字节的字符,第一位为0,后7位为码点,与ASCII相同。
n个字节的字符,第一个字节前面 n 位都是1,n+1位是0,可据此判断有几个字节。后面的几个字节都是 10 为开头2位。
这里规定的都是前缀,对于字符的码点,需要进行截取后依次放入除前缀外的其他位,所以UTF-8又被称为前缀码。格式如表:
字节数 | 码点位数 | 码点范围 | 编码方式 |
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,结果是22909;
22909在UTF-8的3字节数的编码区间 U+0800 (2048) ~ U+FFFF (65535);
22909的二进制值:101100101111101,有15位;
而3字节数的编码需要16位,前面补0,根据表中规则分成3组:0101 100101 111101;
依次填入对应的前缀:11100101 10100101 10111101,得到3个字节;
将得到的三个字节转成十六进制数据:E5 A5 BD,所以汉字 '好' 的UTF-8就是:E5 A5 BD。
encodeURI('好') // '%E5%A5%BD'
UTF-16
码点小于U+FFFF,基本字符,不需处理,直接使用,占两个字节。
否则,拆分成两个码元,四个字节,cp表示码点:低位——((cp - 65536) / 1024) + 0xD800,值范围是 0xD800~0xDBFF;高位——((cp - 65536) % 1024) + 0xDC00,值范围是 0xDC00~0xDFFF。
汉字 '好','好'.codePointAt() // 22909,码点小于U+FFFF,直接进行十六进制转换:579D。表情符号 '',''.codePointAt() // 128516,码点需要拆分:低位:Math.floor(((128516 - 65536) / 1024)) + 0xD800 // 55357, 得到 D83D高位:((128516 - 65536) % 1024) + 0xDC00 // 56836,得到 DE04
String.fromCharCode(0xD83D, 0xDE04) // ''
UTF-32是定长的编码,每个码位使用四个字节进行编码。优点是和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)
前端开发中的编码
字符串长度计算
ASCII码和大部分中文,都是一个码元
而表情字符和其他特殊字符都是两个码元
'a'.length // 1
'好'.length // 1,多数汉字都是基本字符平面,只有一个码元,长度就为1。
''.length // 2
组合字符的长度
'é'.length // 2
'e\u0301'.length // 2
// 获取码点时,忽略了标点符号,显示的是字母的码点
'é'.codePointAt() // 101
'e'.codePointAt() // 101
'é'.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('é') // false
/^\S$/u.test('e\u0301') // false
转义字符
'\x3f'.length // 1
'?'.length // 1
'\x3f'[0] // '?'
'\x3f'.split('') // ['?']
常用API
处理码点和字符
charAt(index)
从一个字符串中返回指定的字符,对于多码元字符,仍会返回码元字符:
'a'.charAt() // 'a'
''.charAt() // '\uD83D'
''.charAt(1) // '\uDE04'
charCodeAt(index)
返回0到65535之间的整数码点值。对于多码元如果字符的码点大于U+FFFF,则返回第一个码元值,还可以加索引参数取后面码元的值。codePointAt(pos)
返回Unicode码点,多码元也能返回完整的码点值。codePointAt可以传入索引参数,对多码元字符取第二个码元值。
// 小于 U+FFFF
'好'.codePointAt() // 22909
'好'.charCodeAt() // 22909
// 大于 U+FFFF
''.charCodeAt() // 55357
''.charCodeAt(1) // 56836
''.codePointAt() // 128516
''.codePointAt(1) // 56836
String.fromCharCode(num1[, ...[, numN]])
返回由指定的UTF-16码点序列创建的字符串。参数范围0到65535,大于65535的数据将被截断,结果不准确。对于多码元字符,则会将两个码元组合得到该字符。String.fromCodePoint(num1[, ...[, numN]])
返回使用指定的代码点序列创建的字符串。可以处理多码元字符的完整码点值。
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()
'é' === 'é' // false
'é' === 'é'.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的区别
URL元字符:分号(;),逗号(’,’),斜杠(/),问号(?),冒号(:),at(@),&,等号(=),加号(+),美元符号($),井号(#)。
encodeURIComponent(';,/@&=') // '%3B%2C%2F%40%26%3D'
encodeURI(';,/@&=') // ';,/@&='
版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。