今天遇到一个情况,就是服务器端是采用的 WebSocket 进行通信的,但是协议是 ASCII
,因此,传输给到我客户端的,是字节流,在 JS 看来,就是 ArrayBuffer。现在面临的问题,就是要要将字节流转换成字符串,然后解析其中的 JSON 信息。之前还没有遇到过,因此就来探究了一下。
ArrayBuffer
根据 MDN 上文档的说明。ArrayBuffer
对象用来表示通用的、固定长度的原始二进制数据缓冲区。
它是一个字节数组,通常在其他语言中称为“byte array”。
也就是说,Buffer 是存储二进制,字节的地方,由其组成的数组;不过,对于我们可以对 ArrayBuffer
看成是不同长度的 Buffer 组成的。
因此,ArrayBuffer 可以理解成其实就是一个个字节组成的序列了。
实际上 ,更详细的描述看这里
关于字符与编码
关于字符,与编码,这里不再多说。更多请看 Python2和3中字符串与编码 一个道理。
我们来明确 JS 两个意思:
- 码点 CodePoint ,指的是一个符号在所采用的字符集中的一个整型值(相当于索引值)
- CharCode,指的是一个编码存储,具体在计算机机内是如何存储的。存储的实现来说,是字节序列。
理论上,我们的 CodePoint
是一个整型,直接在程序中有一个整数来表示不就行了,我们的 CharCode
和 CodePoint
应该相等了是吧?
但事实上不是这样的。为什么呢?因为历史的因素,要考虑到兼容性,内存,传输等方面的原因,所以就做了取舍。不同的编码方式,采用了不同的做法。
编码
编码的定义是指,对于一个字符集中的码点,我们按照什么规则来存储的问题。
编码的转换
我们想要从一种编码转换到另外一种编码,就必须读出码点值,然后根据码点值重新编码。
UTF8 编码
UTF8 变长字节的存储码点,其规则是:
- 单字节字符,最高为设置为 0,后面的 7 位存储码点。
- 对于多字节字符,有几个字节,第一字节的前面几位就设置为 1,后面每个字节的前两位都设置为 10
最后就是这样对应:
Unicode符号范围 | UTF-8编码方式 |
比如对于 Unicode 字符集, UTF8 编码,就采用了 1-6 个字节来将码点,存储在计算机中。
而对于 Unicode 字符集, UCS2 编码,就采用了 2 个字节来将码点存储在内存中;而 UTF16 使用 2 个字节来编码常用字符,用 4 个字节来编码不常用的字符;UTF16 是 utf16 的一个超集。
举个例子:
中 字,Unicode 码点是 4e2d,我们要存储在计算机的时候,采用 utf8 编码,就是将这个码点,用 utf8 映射到字节去,是 e4b8ad 以二进制来对比:
1001110 00101101 Unicode 码点 |
将 utf8 编码后的取出有效字节来就是:
0100 111000 101101 |
JS 中的码点获取
有两个函数:
- String.prototype.charCodeAt() 这个返回的码点最多 2 字节,它表示的是 ucs2/utf16 中的码点值(也就是常用的那些汉字数)
- String.prototype.codePointAt() 这个返回的码点最多 4 字节,返回的表示 Unicdoe 中的码点值
在码点值在 2 字节也内,没有区别。但超过两字节编码的字符的就会出问题了撒
比如这个字:
"𠮷".charCodeAt(0); |
回到我们的问题
对于我们的服务端,采用的是 utf8 编码,(大多数服务器现在都用这个编码),也就是说,它返回的实际上是一系列字节流,这些字节点代表的是字符的 utf8 编码值。
而对于 JS 内部,使用的 utf16 ,两字节来将存储一个码点。那么,我们要让我们传输的字符确实能够在 JS 内正常的使用,就必须转成 utf16 编码。
而我们要了 JS 内的数据传输到服务器的时候,也必须将 utf16 编码转换成 utf8 编码的字节序列。
用 ArrayBuffer 来进行存储。
因此,我就找了 utfx 这个库,这才能实现我想要的目的。
utfx 库
这个函数的实现有点那个啥啊。
他的函数一般都定义成:
utfx.UTF16toUTF8(src, dst); |
这个形式,其实 src 每调用一次就会返回一个元素,然后转换后,就以转换后的结果为参数调用 dst
这和 LUA 中 ltn12 中的 sink/source/pump 的概念累似