讲解之前我们先了解一些基本概念,什么是字符集?字符集就像一张表,这个表里面有很多的符号,每个符号都可以通过下标(这个符号在表的中的序号)对应到,有点类似js中的map,例如97这个下标代表的就是符号a。当然,字符集是有很多种,最常见的就是ASCII字符集了,还有中文的GB2312字符集,每个字符集其实就是一张表,上面有各种符号,然后通过下标和它们对应。当然,最全的,包含所有符号的字符集是Unicode,今天讲的字符都是基于Unicode字符集的。

有了字符集,我们就可以利用下标去对应符号了,但是一个字符集中有很多的字符,这样有些下标就会很长了,如14324,17423等,因此要对这些下标进行编码来节省空间,编码方式有utf-8,utf-16和Unicode编码等。

在js中,最常见的就是Unicode编码了(注意:Unicode字符集和Unicode编码是两个不同的概念),第一种情况,它以\x开头,后面加上两个十六进制表示单字节字符,如'\x61'表示的就是字符a,怎么来的呢,因为十六进制的61代表十进制的97,而97下标代表的就是a符号;第二种情况,它以\u开头,后面加4个十六进制表示一个字符,例如'\u0061',这个表示的也是字符a,和\x不同,\u每次利用两个字节表示一个字符(一个字节表示可以表示两个十六进制),\x则使用一个字节,因此\x只能表示255个符号,而\u可以表示65535个字符。

因此有的时候我们会遇到如何识别单字节和双字节的问题,我们就可以利用\x或者\u来判断:

测试是否是单/双字节,主要看字符的序号是否大于255,如果大于255,那么一个字节就存不下了,因为一个字节8位,最多放序号位255的字符。

当然,在js中,除了Unicode编码,还有一种八进制的转义字符,如'\141'表示的也是字符a,注意,和上面不同,这里用的是八进制,因为141的八进制代表的是十进制的97,所以取的是下标为97的符号。需要注意的是,它只能表示255个符号,最大为'\377',同时注意例如'\128'表示的是两个字符,第一个为八进制转移字符:'\12',另一位为'8',因为八进制里面最大的是7,没有8。

除了在js中存在这种编码方式,在html和css中也是存在的。在html中,&#+十进制+;可以表示一个符号,例如a表示的就是符号a,或者&#x+十六进制+;也可以表示,如a表示的也是a,只不过一个用的是下标的十进制一个是十六进制。注意,因为html的这种隐藏编码方式,会导致我们有的时候踩到坑, &符号的分割拼接陷阱

在css中,主要是提现在伪元素后面的content中,例如:

这里的content其实是符号a,在css里中,\后面加上下标的十六进制即可表示这个字符。

为了方便大家如何查编码,进制转换,总结了一些常用的获取以及转换的方法:

上面讲了字符中的一些基本知识,在字符集中还有两个特殊的东西,一个是中文,一个是emoji。

中文在Unicode字符集中的分布比较广泛:

上图只是一部分分布,但是主要的中文集中在\u4e00-\u9fa5之间,因此有的时候会遇到验证是否存在中文:

注意啊,上面只是验证绝大多数的中文,不是完整的验证,例如中文中的"𠮷"就无法通过,因为它的下标不在这个范围之内。

emoji就是Unicode字符表中的表情,这玩意的在Unicode字符表中很靠后的位置,下标排在65535之后,这回导致一个什么问题呢?上面我们说到在js中,可以用\u加上十六进制表示字符,但是这个最大只能表示\uFFFF,也就是下标为65535的字符,无法表示更大的字符,因此,在这个时候,就需要用两个\u来表示了,例如😂,它就是用'\uD83D\uDE02'表示的。

因此一个emoji需要4个字节(一个中文需要两个字节),我们平常用的String.charCodeAt(0)就无法获取它的正确下标了,例如'😂'.charCodeAt(0)就是55357,它获取的是第一个\u的十进制,因此获取emoji的正确下标需要用codePointAt方法,'😂'.codePointAt(0)为128514。(恢复用String.fromCodePoint方法),那平常我们也会遇到验证emoji,代码如下:

现在来讲讲utf-8编码,utf-8编码也是用来编码下标的,它先计算出下标的二进制,然后根据它的长度把它们塞到一个特殊序列的二进制中:

1字节 0xxxxxxx (0-127)

2字节 110xxxxx 10xxxxxx (128-2047)

3字节 1110xxxx 10xxxxxx 10xxxxxx (2048-65535)

4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (65536-1114111)

5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

什么意思呢?假如汉字'我',下标为25105,在范围2048-65535之内,因此我们要把25105的二进制拆分成三部分(4-6-6):0110 001000 010001,然后在第一部分前面加1110,第一部分变成11100110,第二部分前面加10,第二部分变成10001000,第三部分前面加10,第三部分变成10010001,因此小标25105被编码成了三个字节,分别是11100110 10001000 10010001,转成十六进制也就是e6 88 91。但是注意,这里的e6 88 91我们是不能用\u去用的,因为\u是Unicode编码,因此你写\ue68891是无法识别的。

那utf-8在js的什么地方有用到呢?在encodeURI或者encodeURIComponent的时候用到,例如我们encodeURI(‘我'),那么结果为%E6%88%91,其中的三个十六进制就是我们刚刚计算出来的那三个。与之类似的有一个escape方法,我们escape('我')发现是%u6211,这个其实就是%u+字符的十六进制,和\u6211这个类似。

但是我们可以发现encodeURI或者encodeURIComponent在进行utf-8编码的时候其实是将二进制变成了十六进制,这样其实结果会变长3倍,'我'这个汉字在encodeURI或者encodeURIComponent编码后变成了%E6%88%91,变成了9个字符,其实正常只会变成三个字符(下标为230,136,145的字符),虽然这些字符可能肉眼看不到,但是它们也是确实的字符。因此,在某些情况下,我们还是希望有一个将字符进行utf-8编码/解码的方法,下面就是我写的utf-8编码/解码的js方法

这里面有很多二进制的或,且,移位操作,我们先来看如何进行utf-8编码:

background Layer 1 ‘我’这个汉字的下标是25105 25105的二进制是110001000010001 要把它塞入1110**** 10****** 10******的格式中 第一步,我们先来塞第一个1110****,我们可以发现,后面还有12个没塞的,因此我们将110001000010001 先向右移动12位,这样就把没匹配的12个先屏蔽了,移动后就是110,再让这个数字和11100000(224)或操作 因为或操作这只要有一个1就是1,224的二进制11100000后四位都是0,和110或操作后就是11100110(230), 第二步,我们来塞10******,同里,我们先向右移6位(>>6),屏蔽最后6位数字,也就是110001000,然后和 111111(63)执行且操作,保留最后6位中的1,操作完后就是001000,再和10000000(128)执行或操作, 操作后就是10001000(136) 第二步,我们来塞第二个10******,这是我们直接和111111(63)且操作,保留最后6位,就是10001,然后和 10000000(128)或操作,结果是10010001(145)

我们再看看看如何解码:

background Layer 1 上回我们计算得到‘我'这个汉字utf-8编码后得到下标分别位230,136,145的字符 我们可以知道在utf-8里面第一个开头的字符都是不一样的,因此我们分别用它们去和11000000(192), 11100000(224),11110000(240)去执行且操作,如果结果是192(224,240)的话,那么就可以知道它们 是那种模式了。 因为230的二进制是11100110,因此可知它们的模式是1110**** 10****** 10******,我们要想办法得到将 这三个字符中的省略号得到,获取完整二进制。 我们先看230的,我们知道11100110的最后四位肯定是完整二进制的头四位,因此我们先将11100110 和1111(15)执行且操作,保留后四位,然后右移动12位,结果是0110 000000 000000 我们搞定了头四位,现在弄中间的六位,中间的下标位136(10001000),这里,我们先和111111(63) 执行且操作,保留最后6位,再右移6位,同时和之前的结果或操作,结果是0110 001000 000000 现在来弄最后6位,145(10010001),同理,先和111111(63)执行且操作,保留最后6位,再和之前的 结果或,结果是0110 001000 010001,它的十进制是25105,也就是'我'这个汉字

其实通过也可以通过先用encodeURI或encodeURIComponent进行编码,然后将%十六进制变成十进制,不过那样的话还需要处理一些额外的符号,例如%或者&等,因为encodeURI或encodeURIComponent默认是会给它们编码的。

其他文章

0
我要评论

评论

返回
×

我要评论

回复:

昵称:(昵称不超过20个字)

图片:

提交
还可以输入500个字