UTF-16

✍ dations ◷ 2025-04-29 21:38:20 #Unicode转换格式

UTF-16是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为"storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。

UTF是"Unicode/UCS Transformation Format"的首字母缩写,即把Unicode字符转换为某种格式之意。UTF-16正式定义于ISO/IEC 10646-1的附录C,而RFC2781也定义了相似的做法。

Unicode的编码空间从U+0000到U+10FFFF,共有1,112,064个码位(code point)可用来映射字符。Unicode的编码空间可以划分为17个平面(plane),每个平面包含216(65,536)个码位。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0),其他平面称为辅助平面(Supplementary Planes)。基本多语言平面内,从U+D800到U+DFFF之间的码位区段是永久保留不映射到Unicode字符。UTF-16就利用保留下来的0xD800-0xDFFF区块的码位来对辅助平面的字符的码位进行编码。

第一个Unicode平面(码位从U+0000至U+FFFF)包含了最常用的字符。该平面被称为基本多语言平面,缩写为(Basic Multilingual Plane,BMP)。UTF-16与UCS-2编码这个范围内的码位为16比特长的单个码元,数值等价于对应的码位。BMP中的这些码位是仅有的可以在UCS-2中表示的码位。

辅助平面(Supplementary Planes)中的码位,在UTF-16中被编码为一对16比特长的码元(即32位,4字节),称作(Surrogate Pair),具体方法是:

上述算法可理解为:辅助平面中的码位从U+10000到U+10FFFF,共计FFFFF个,即220=1,048,576个,需要20位来表示。如果用两个16位长的整数组成的序列来表示,第一个整数(称为前导代理)要容纳上述20位的前10位,第二个整数(称为后尾代理)容纳上述20位的后10位。还要能根据16位整数的值直接判明属于前导整数代理的值的范围(210=1024),还是后尾整数代理的值的范围(也是210=1024)。因此,需要在基本多语言平面中保留不对应于Unicode字符的2048个码位,就足以容纳前导代理与后尾代理所需要的编码空间。这对于基本多语言平面总计65536个码位来说,仅占3.125%。

由于前导代理、后尾代理、BMP中的有效字符的码位,三者互不重叠,搜索是简单的:一个字符编码的一部分不可能与另一个字符编码的不同部分相重叠。这意味着UTF-16是自同步(self-synchronizing)的:可以通过仅检查一个码元来判定给定字符的下一个字符的起始码元。UTF-8也有类似优点,但许多早期的编码模式就不是这样,必须从头开始分析文本才能确定不同字符的码元的边界。

由于最常有的字符都在基本多文种平面中,许多软件处理代理对的部分往往得不到充分的测试。这导致了一些长期的bug与潜在安全漏洞,它们甚至存在于广为流行且评价颇高的应用软件中。

Unicode标准规定U+D800...U+DFFF的值不对应于任何字符。

但是在使用UCS-2的时代,U+D800...U+DFFF内的值被占用,用于某些字符的映射。但只要不构成代理对,许多UTF-16编码解码还是能把这些不符合Unicode标准的字符映射正确的辨识、转换成合规的码元。按照Unicode标准,这种码元序列本来应算作编码错误。

以U+10437编码(�)为例:

假设要将U+64321(16进制)转成UTF-16编码。因为它超过U+FFFF,所以他必须编译成32位(4个byte)的格式,如下所示:

V = 0x64321Vx = V - 0x10000= 0x54321= 0101 0100 0011 0010 0001Vh = 01 0101 0000 // Vx的高位部份的10 bitsVl = 11 0010 0001 // Vx的低位部份的10 bitsw1 = 0xD800 //結果的前16位元初始值w2 = 0xDC00 //結果的後16位元初始值w1 = w1 | Vh= 1101 1000 0000 0000 |       01 0101 0000= 1101 1001 0101 0000= 0xD950w2 = w2 | Vl= 1101 1100 0000 0000 |       11 0010 0001= 1101 1111 0010 0001= 0xDF21

所以这个字U+64321最后正确的UTF-16编码应该是:

0xD950 0xDF21

而在小尾序中最后的编码应该是:

0x50D9 0x21DF

因为这个字超过U+FFFF所以无法用UCS-2的格式编码。

UTF-16比起UTF-8,好处在于大部分字符都以固定长度的字节(2字节)存储,但UTF-16却无法兼容于ASCII编码。

UTF-16的大尾序和小尾序存储形式都在用。一般来说,以Macintosh制作或存储的文字使用大尾序格式,以Microsoft或Linux制作或存储的文字使用小尾序格式。

为了弄清楚UTF-16文件的大小尾序,在UTF-16文件的开首,都会放置一个U+FEFF字符作为Byte Order Mark(UTF-16 LE以 FF FE 代表,UTF-16 BE以 FE FF 代表),以显示这个文本文件是以UTF-16编码,其中U+FEFF字符在UNICODE中代表的意义是 ZERO WIDTH NO-BREAK SPACE,顾名思义,它是个没有宽度也没有断字的空白。

以下的例子有四个字符:“朱”(U+6731)、半角逗号(U+002C)、“聿”(U+807F)、“�”(U+2A6A5)。

UTF-16可看成是UCS-2的父集。在没有辅助平面字符(surrogate code points)前,UTF-16与UCS-2所指的是同一的意思。但当引入辅助平面字符后,就称为UTF-16了。现在若有软件声称自己支持UCS-2编码,那其实是暗指它不能支持在UTF-16中超过2字节的字集。对于小于0x10000的UCS码,UTF-16编码就等于UCS码。

Windows操作系统内核中的字符表示为UTF-16小尾序,可以正确处理、显示以4字节存储的字符。但是Windows API实际上仅能正确处理UCS-2字符,即仅以2字节存储的,码位小于U+FFFF的Unicode字符。其根源是Microsoft C++语言把 wchar_t 数据类型定义为16比特的unsigned short,这就与一个 wchar_t 型变量对应一个宽字符、可以存储一个Unicode字符的规定相矛盾。相反,Linux平台的GCC编译器规定一个 wchar_t 是4字节长度,可以存储一个UTF-32字符,宁可浪费了很大的存储空间。下例运行于Windows平台的C++程序可说明此点:

// 此源文件在Windows平台上必须保存为Unicode格式(即UTF-16小尾)// 因为包含的汉字“�”,不能在简体中文版Windows默认的代码页936(即GBK)中表示// 该汉字在UTF-16小尾序中用4个字节表示// Windows操作系统能正确显示这样的在UTF-16需用4字节表示的字符// 但是Windows API不能正确处理这样的在UTF-16需用4字节表示的字符,把它判定为2个UCS-2字符#include <windows.h>int main(){	const wchar_t lwc = L"�";	MessageBoxW(NULL, lwc, lwc, MB_OK);	int i = wcslen(lwc);	printf("%d\n", i);	int j = lstrlenW(lwc);	printf("%d\n", j);	return 0;}

Windows 9x系统的API仅支持ANSI字符集,只支持部分的UCS-2转换。1996年发布的Windows NT 4.0的API支持UCS-2。Windows 2000开始,Windows系统API开始支持UTF-16,并支持Surrogate Pair;但许多系统控件比如文本框和label等还不支持surrogate pair表示的字符,会显示成两个字符。Windows 7及更新的系统已经良好地支持了UTF-16,包括Surrogate Pair。

Windows API支持在UTF-16LE(wchar_t类型)与UTF-8(代码页CP_UTF8)之间的转码。例如:

相关

  • 制霉菌素6-dideoxy-β-L-mannopyranosyl)oxy]-1, 3,4,7,9,11,17,37-octahydroxy-15, 16,18-trimethyl-13-oxo-14, 39-dioxabicyclononatriaconta-19, 21,25,27,29,31-hexaene-36-ca
  • 760110 数学 120 信息科学与系统科学 130 力学 140 物理学 150 化学 160 天文学 170 地球科学 180 生物学210 农学 220 林学 230 畜牧、兽医科学 240 水产学310 
  • 白蛉白蛉(Sandfly)是蛾蚋科的一个亚科,包含了许多以吸血为生的物种。白蛉在美国有时也等同虻或蠓科。在美国之外地区,白蛉指在白蛉亚科(Phlebotominae)成员。新西兰白蛉则是属于蚋的一
  • 没有代表权不能征税“无代表,不纳税(英语:No taxation without representation)”起源于1750年代和1760年代,最早由Jonathan Mayhew提出。此口号概述了英国十三殖民地人民的不满,这也是美国革命的主
  • 玛莲娜·迪特里茜玛丽·玛德莲娜·“玛琳”·黛德丽(德语:Marie Magdalene "Marlene" Dietrich,1901年12月27日-1992年5月6日), 德国演员兼歌手,拥有德国与美国双重国籍,在其近七十年的演艺生涯中持
  • 泰国人口泰国人口中泰国主体民族泰族占75%。泰族人口分布:中央泰族(占总人口的33.7%,包括曼谷),东北方的老族(34.2%),北方泰族(18.8%),还有南方泰族(13.3%)。中央泰国人讲的方言作为官方
  • 硝酸钐硝酸钐(化学式: Sm(NO3)3)是钐的硝酸盐,为淡黄色易溶于水的化合物。硝酸钐可以借由将硝酸与氢氧化钐反应生成:硝酸钐六水合物加热至90°C熔化,在125°C失去一分子结晶水,继续加热逐
  • 内蒙古方言内蒙古方言(另作中国蒙古语中部方言、内蒙古牧业方言,简称内蒙方言)是语言学家对中国境内(尤其指内蒙古自治区境内)或漠南非喀尔喀的蒙古语方言的统称。美国国际语言暑期学院则以
  • 爱媛县第2区爱媛县第2区是日本众议院的选区,始于1994年。北海道 13 | 山形县 4 | 静冈县 9 | 岛根县 3 | 大分县 4福井县 3 | 山梨县 3 | 德岛县 3 | 高知县 3 | 佐贺县 3青森县 4 | 岩
  • C-295运输机C-295运输机是一种双发战术运输机,现由位于西班牙境内的空中客车国防航天公司制造。1997年首飞,第一支订购C-295的部队是西班牙空军。C-295的单位造价为2,800万美元(2013年),用户