写在前面的话
本文属于 字符编码系列文章之一,更多请前往 字符编码系列。
大纲
不同编码转换的理论基础
UTF-16转UTF-8
UTF-16转GBK
UTF-16和UTF-8之间的转换
UTF-16和GBK之间的转换
不同编码转换的理论基础
不同的编码直接如何转换的,这里先简单的描述下UTF-16、UTF-8、GBK直接的转换过程。
由于本文是基于JavaScript的,而JS现在的编码可以认为是UTF-16,所以都会经过UTF-16中转。
UTF-16转UTF-8
这两者都是Unicode,所以有一个大前提就是码点一致,仅仅是对于码点的编码方式不一致而已,因为UTF-16可以认为是固定2字节的实现(4字节的比较少见),所以参考如下Unicode和UTF-8转换关系表即可:
Unicode编码
UTF-8字节流
U+00000000 - U+0000007F
0xxxxxxx
U+00000080 - U+000007FF
110xxxxx 10xxxxxx
U+00000800 - U+0000FFFF
1110xxxx 10xxxxxx 10xxxxxx
U+00010000 - U+001FFFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U+00200000 - U+03FFFFFF
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U+04000000 - U+7FFFFFFF
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
所以UTF16和UTF8之间的相互转换可以通过上表的转换表来实现,判断Unicode码所在的区间就可以得到这个字符是由几个字节所组成,之后通过移位来实现,分为新的多个字节来存储。
UTF-16转GBK
UTF-16和GBK直接的转换就稍微复杂点,因为Unicode和GBK的码点不一致,因此需要GBK个Unicode的码点映射关系表才能进行相应转换。
这里GBK和Unicode的码点映射表由于太长了,就不单独列出来,可以参考:Unicode编码和GBK的转换映射表 (如果链接不可用可以自动搜索或者参考博客中的源码链接)
然后通用,拿到Unicode码点后可以根据映射表转换为GBK码点,然后用GBK的编码方式编码即可完成转换
UTF-16和UTF-8之间的转换
UTF-16转UTF-8
步骤描述
Step1:获取该字符对应的Unicode码
Step2:判断该Unicode码所在的范围,根据不同的范围,来决定存储它的字节长度。
如果介于U+00000000 – U+0000007F之间,代表该字符采取一个字节存储,那么直接通过这个新字节的unicode码,即可转换为UTF-8码(这是这里的一种简称,不同的编程语言有不同实现,例如可以用两个字节来存储一个字符的信息,解码时进行判断,如果发现是UTF-8的多字节实现,那么将多字节合并后再转为一个字符输出).转换完毕
如果介于U+00000080 – U+000007FF之间,代表该字符采取两个字节存储,那么将该Unicode码转为二进制,取出高5位(这里不分大端序和小端序,只以实际的码为准,具体实现可以采取移位实现),并加上头部110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节。然后分别通过两个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
如果介于U+00000800 – U+0000FFFF之间,代表该字符采取三个字节存储,那么将该Unicode码转为二进制,取出高4位,并加上头部1110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节;再取出低6位(按顺序取),加上头部10,组成第三个字节。然后分别通过三个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
如果介于U+00010000 – U+001FFFFF之间,代表该字符采取四个字节存储(实际上,四个字节或以上存储的字符是很少的),那么将该Unicode码转为二进制,取出高3位,并加上头部11110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节;再取出低6位(按顺序取),加上头部10,组成第三个字节;再取出低6位(按顺序取),加上头部10,组成第四个字节。然后分别通过四个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
如果介于U+00200000 – U+03FFFFFF,代表该字符采取五个字节存储,那么将该Unicode码转为二进制,取出高2位,并加上头部111110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节;再取出低6位(按顺序取),加上头部10,组成第三个字节;再取出低6位(按顺序取),加上头部10,组成第四个字节;再取出低6位(按顺序取),加上头部10,组成第五个字节。然后分别通过五个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
如果介于U+04000000 – U+7FFFFFFF,代表该字符采取六个字节存储,那么将该Unicode码转为二进制,取出高1位,并加上头部1111110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节;再取出低6位(按顺序取),加上头部10,组成第三个字节;再取出低6位(按顺序取),加上头部10,组成第四个字节;再取出低6位(按顺序取),加上头部10,组成第五个字节;再取出低6位(按顺序取),加上头部10,组成第六个字节。然后分别通过六个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
代码实现
/**
* @description 将utf-16编码字符串转为utf-8编码字符串
* @param {String} str 传入的 utf16编码字符串(javascript内置的就是utf16编码)
* @return {String} utf8编码的字符串,js打印会有乱码
*/
exports.utf16StrToUtf8Str = function(str) {
if (!str) {
// ''字符属于ascii码,所以不必担心不同编码的转换问题
return