Unicode 编码概念
Unicode 编码概念
Unicode 编码可能是我们日常开发中接触最多的字符编码方式之一,其它常见的中文编码方式还包括 GB2132-80 / GB13000 / GBK / GB18030 。在大部分的开发中,最常见、最常用的还是 Unicode ,它在各种编程语言中的支持相对比较完善,现在的网站和BS架构的应用,基本上都使用 Unicode 。因此,除非是特定的国际化需要, Unicode 能满足大部分的开发需要。
但要注意,Unicode 这个名称本身只是一个笼统的称呼。更深入细节来说,它包括几个编码格式: utf-8 / utf-16 / utf-32
utf-16
utf-16 是最早提出的 Unicode 编码方式,它采用 16 位来表示一个字符,理论上支持 65536 个字符。 支持 Unicode 的编程语言,像 java / C# 等,采用的是 utf-16 的编码格式。
采用定长的方式来表示字符,会带来计算速度方面的优势,在进行比较操作、字符定位操作时可以很容易通过计算直接定位。
但是 16 位的长度对于存储 ASCII 的字符来说就浪费了,因为它们本身只需要 7 位就能完全表示出来。如果用在存储数据方面,那么采用 utf-16 就会造成很多空间浪费。
utf-8
utf-8 并不是使用 8 位来表示一个字符。 utf-8 是一种变长的编码方式,也就是说,它可以根据需要,用 8 位、 16 位或 24 位来表示一个字符。在 ASCII 范围的字符,在 utf-8 中编码是相同的,都采用 8 个位 ( 只使用 7 个有效位 )。而汉字字符,由于数量多,那么需要采用 16 或 24 位。
与 utf-16 相比, 在混合存储数据时,一般 utf-8 可以节省存储空间,同时满足多语言的需求。因此,常见的文件编码格式、数据库存储字符编码,采用 utf-8 。以数据库为例,uuid/email/phone 等常见的数据字段基本上都是英文、数字、简单符号,同时会混合住址、备注资料等中文内容,因此一般会采用 utf-8 (同时,utf-8 没有字节序问题,不容易产生混淆)
而 utf-8 进入到程序处理时,就需要转换为编程语言内置的 utf-16 。这个转换是经过算法优化的,不需要担心性能问题。转换之后,才能在编程语言中进行相应的字符处理。
utf-32
utf-32 也属于定长的编码方式,使用 32 位表示一个字符。它可以表示更大范围的字符。但就目前的应用而来看,一般采用 utf-16 就足够,支持 utf-32 的系统和程序并不普遍。
字节序问题
在具体的实现上,定长字节的编码方式会存在一个字节序的问题,这个问题类似于网络字节序问题。字节序问题源于在发送和接收的过程中,一个整个的结构如何切分为字节,如何把字节重新还原为原有的结构。
utf-8 是基于字节的,它是基本的传输单位,因此 utf-8 不存在字节序问题,发送者和接收者都是按顺序一个字节一个字节处理的。但对于 utf-16 和 utf-32 , 在传送时应该先传送那个字节,就会成为一个问题。如果双方没有约定,接收者无法确定发送者是先传高字节或是低字节,因此接收者就没有办法组织字节顺序构建正确的字符。而为了让这个问题显得更复杂,专家们脑洞大开,不同的系统平台上采用了不同的字节序的处理方法 😃
因此,针对字节序问题,在 Unicode 中形成约定,在开始处加上字节序标记 Byte Order Mark , BOM:
对 utf-16:
使用 FF FE 开头,为小数端 utf-16-le ( 小数在后 )
使用 FE FF 开头,为大数端 utf-16-be ( 大数在后 )
对 utf-32:
使用 FF FE 00 00 开头,为小数端 utf-32-le ( 小数在后 )
使用 00 00 FE FF 开头,为大数端 utf-32-be ( 大数在后 )
另外, utf-8 默认加上 EF BB BF
在打开文件上要小心,当使用 utf-8 方式打开文件时,如果文件缺少 BOM , 某些程序会“很聪明”地在文件开头加上 BOM 。这个“聪明”的设计曾经给我带来过问题,在数据库导出之后,再导入时发现了开始的数据出现乱码,通过 16 进制编辑器一看,发现数据文件头部被加了 BOM ~~