之前一直使用第三方的NPM包对文件上传做处理,上周在了解具体的实现原理中,遇到了编码方面的问题
,然后又去了解了编码。
Ascii,GBK,Unicode 字符集
Ascii
Ascii(美国信息交换标准代码),它是一套电脑编码系统,使用连续的字节状态来表示英文文字。Ascii是一种单字节的编码系统,以至于最多只能有2的八次方种状态。Ascii是美国人的标准,并不能把全世界的语言都表示出来。最后出现了很多其他的编译系统,比如我们的GBK。
GBK
为了适应中国自己的需求,我们发明了一种GB2312编码:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE。但是中国的汉字太多了,还是没法满足我们的需要。于是在GB2312编码上进行扩展,只要第一个字节表示大于127就表示汉字,这种编码方式就是GBK。
Unicode
当时各国都在各种搞一套,导致编码标准不统一。这时候国际标谁化组织开始统一标准,Unicode出现了。Unicode中对Ascii的127以下的编码方式保持不变,其他的重新进行编码。其中,Unicode目前普遍采用的是UCS-2(每2个字节表示一个符号),这导致之前使用Ascii表示的符号多出了1个字节。Unicode并不完美。直到互联网的出现,为解决unicode如何在网络上传输的问题UTF出现,这也是我们要重点讲解的编码。
Ascii,GBK,Unicode 和 UTF,UCS 区别
Ascii,GBK,Unicode准确来说应该叫做字符集,对每个字符使用对应的唯一的代码值来表示,并没有规定使用多少个字节来表示。UTF,UCS是基于Unicode字符集的编码方式,使用对应的字节来表示字符。
UTF 编码
早期Unicode版本中,UTF分为UTF-8,UTF-16,后来又有了UTF-32。UTF-8并不是表示一个字符用一个字节表示,UTF-8使用可变的字节数来表示一个字符。根据字节中开头的bit标志来识别使用1~4个字节来表示一个字符。UTF-16表示使用固定的2个字节来表示任何的字符。UTF-32使用4个字节表示任意字符。
UTF-8 编码详解
UTF-8是可变的字节编码,规则如下:
可以看到,UTF-8中开头的bit是标志信息。除去这些标志信息,UTF-8中一个字节只能表是2的7次方(128)个字符,两个字节只能表示2的11次方(2048)个字节,三个字节只能表示2的16次方(65536)个字节,四个只能表示2的21次方(2097152)个字节。其实在早期UTF-8可以到达6个字节序列,后来被RFC 3629重新规范,只能使用原来Unicode定义的区域而Unicode6.1定义范围为0到0x10FFFF,也就是0到2的21次方。UFT-8就只能到4个字节序列了。
UTF编码表示字符
因为中、日、韩的三种文字占用了Unicode(UCS-2)中0x3000到0x9FFF的代码值,所以需要使用3个字节的utf-8来显示,而只需要2个字节的uft-16显示。
可以看出玉
在Unicode(UCS-2)中代码值为29577,UTF-8使用3个字节表示,UFT-16中使用了2个字节表示。a
代码值为97,UTF-8使用1个字节表示,UFT-16中使用了2个字节表示。
UTF-8和UTF-16优劣势
我们从这里可以看出:
- UTF-8的优势在于对英文编码和Ascii一样只用一个字节表示,而UTF-16需要浪费一个字节。
- 而对应汉字UTF-8需要使用3个字节表示,而UTF-16就能节约一个字节。
- UTF-16不能像UTF-8对小于127代码值的字符友好的兼容Unicode。
判断UTF-8,UTF-16编码
很多场景下我们需要去判断字符编码格式,比如文件上传。
方法一
有的文件会自动添加BOM到文件头,我们可以通过它来判断文件编码格式。但是,首先不能保证所有的文件都带有BOM,其次BOM头添加导致无法实现对ASCII的兼容。
BOM标志:
简单判断代码:
方法二
通过对文件的每一个字节进行判断,来区分UTF编码,但是这个也不能保证完全准确。
比如: 文件中所有字符的UTF-16编码都为110xxxxx 10xxxxxx
,就无法区分UTF编码。
|
|
注意:在判断时记住,无论一个字符使用多少个字节序列表示,都无法小于对应的代码值。比如:
判断2个字节的时候使用0xC2 <= bytes[i] && bytes[i] <= 0xDF) && (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF)
,而根据规则(110xxxxx 10xxxxxx)好像应该使用0xC0 <= bytes[i] && bytes[i] <= 0xDF) && (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF)
但是UTF-8中2个字节表示的代码值应该大于2的11次方,所以第一种才是正确的。