UTF-8 and Unicode FAQ for Unix/Linux
2007-08-30 20:20
1026 查看
UTF-8andUnicodeFAQforUnix/Linux
作者原文地址:http://www.cl.cam.ac.uk/~mgk25/unicode.html
译者:
译文地址:http://blog.csdn.net/lovekatherine/archive/2007/08/30/1765903.aspx
转载时务必以超链接形式标明文章原始出处及作者、译者信息。
本文对在POSIX系统(Linux,Unix)中如何应用Unicode/UTF-8这一话题进行了全面讲解,不仅为普通用户提供介绍性信息,也为富有经验的开发者提供详尽的参考。
Unicode已经开始在各个层面上取代ASCII,ISO8859以及EUC。它不仅允许用户处理地球上的任何文字和语言,而且还支持一套范围广泛的数学和技术符号集,以用于简化科学信息的交流。
通过使用UTF-8这种编码方式,原本完全围绕ASCII设计的环境,例如Unix,也可以方便且向后兼容的应用Unicode。UTF-8就是Unix,Linux以及类似系统中Unicode的应用形式。现在是时候该对它进行深入了解,并保证你的软件对UTF-8编码方式提供良好支持。
目录
什么是UCS和ISO10646?
国际标准ISO10646定义了通用字符集(UCS)。UCS是其它所有现有字符集标准的超集,它保证与其它字符集之间的双向兼容性。这意味着如果你将任意字符串转换为UCS格式再转换为源格式,不会出现信息的丢失。UCS中包含了用于表示所有已知语言的字符。这不仅包括拉丁语、希腊语、斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语和格鲁吉亚语,还包括中文、日文和韩文这样的表意文字,以及平假名、片假名、Hangul、梵文、孟加拉语、果鲁穆奇语、古吉拉特语、奥里雅语,泰米尔语、泰卢固语、埃纳德语、马拉雅拉姆语、泰国语、老挝语、高棉语,、汉语拼音、藏语、Runic(古代北欧文字)、埃塞俄比亚语、CanadianSyllabics、切罗基语、蒙古语、欧甘语、缅甸语、僧伽罗、Thaana、彝语和其它语言。对于上面未提及的语言,对其如何编码的研究正在进行中,并最终都会被加入UCS。这其中不仅包括古语言如
ISO10646原本被定义了一个31-bit的字符集。元素个数为216,且元素(用32-bit的整型数值表示)之间只有低16位不同的元素子集,被定义为UCS的一个平面。
最常用的字符,包括各主要旧有编码标准中的所有字符,全部被放入了UCS的第一个平面中(值域为0x0000至0xFFFD),该平面又被称为基本多语言平面(BMP)或平面0。位于BMP之外的字符大多数都是用于特殊应用,例如古代文字和科学符号。按照目前的计划,永远不会有字符被分配到定义为从0x000000至0x10FFFF、可容纳超过100万个潜在未来字符的21-bit编码空间之外。ISO16046-1标准最初于1993被公布,它定义了UCS这个字符集的结构以及BMP的内容。2001又增加了ISO10646-2标准,定义了BMP意外的字符编码。2003年,这两部分被合并为单一的ISO10646标准。新的字符仍然被不断的添加进入UCS,但是已有的字符会保持稳定,不会发生变化了。
UCS为每个字符不仅分配了码值,还有一个正式名称。一个表示UCS或Unicode码值的16进制数通常在表示时加上前缀"U+",例如U+0041表示拉丁大写子母A的UCS码值。码值在U+0000至U+007F之间的UCS字符与US-ASCII(ISO646IRV)相同,码值在U+0000至U+00FF之间的字符与ISO8859-1(Latin-1)相同。码值在U+E000至U+F8FF之间,以及BMP之外的UCS字符是被保留的、留作私用的。UCS还定义了将UCS字符串编码为字节串的几种方法,例如UTF-8和UTF-16。
UCS标准的完整引用形式为:
InternationalStandardISO/IEC10646,Informationtechnology—UniversalMultiple-OctetCodedCharacterSet(UCS).Thirdedition,InternationalOrganizationforStandardization,Geneva,2003.
可从ISO
[更新]2006年9月,ISO在它的
什么是组合字符?
UCS中的一些码值被分配给了组合字符。它们类似于打字机上的不占空位的重音键。组合字符自身不是一个完整的字符。它是一个重音或其它指示标记,用于添加到之前的字符之上。这样,就可以为任何字符添加重音。那些最重要的加重音的字符,就像普通语言的正字法(orthographiesofcommonlanguages)中用到的那样,在UCS中有自己独立的位置,来保证其与旧字符集的向后兼容性。这些字符被称为预制字符。UCS提供预制字符是为了与那些未定义组合字符的旧编码集,例如ISO8859,保持向后兼容性。组合字符机制允许用户为任何字符添加重音或其它指示标记。这对于科学标记尤其重要,例如数学方程式以及国际音标字母表,可能会需要在一个基本字符后组合上一个或多个指示标记组合字符位于其要修饰的字符之后。例如,德语中的元音变音字符Ä(带分音符的拉丁字母A),可以用码值为U+00C4的UCS预制字符来表示,也可以通过普通拉丁字母A后跟组合分音子符的组合U+0041U+0308来表示。当需要堆叠多个重音符,或在一个基本字符的上面和下面都要加上组合标记时,可以使用多个组合字符;比如在泰国文中,一个基本字符最多可加上两个组合字符。
什么是UCS实现级别?
无法指望所有系统都支持UCS中的全部高级机制,例如组合字符。因此,ISO10646定义了以下三种实现级别:级别1
不支持组合字符和HangulJamo字符
级别2
类似于级别1,但是对于某些语言,允许一个固定列表中的组合字符(例如,希伯来语,阿拉伯语,梵文,孟加拉语,果鲁穆奇语,古吉拉特语,奥里雅语泰米尔语,泰卢固语,埃纳德语,马拉雅拉姆语,泰国语,老挝语)。
如果具体实现中没有对最起码的几个组合字符的支持,就无法使用UCS完整地表达这些语言。
级别3
支持所有UCS字符;举例来说,这样数学家就可以对任意字符加一个波浪或一个箭头或是两者兼有。
USC是否被采纳为国家标准?
是的,很多国家已经采纳ISO10646作为国家标准,某些情况下在原有标准基础上添加了对旧国家标准的交叉引用,实现指引以及定义各种实现子集。中国:GB13000.1-93
日本:
韩国:KSX1005-1:1995(包含ISO10646-1:1993的修正案1-7)
越南:
伊朗:
什么是Unicode?
上世纪80年代后期,两个尝试创立统一字符集的项目同时独立存在。一个是Unicode标准可以像任何普通书籍一般被订购,例如通过
TheUnicodeConsortium:
Addison-Wesley,2006,
ISBN0-321-48091-0.
如果你的工作频繁的与字处理和字符集打交道,你绝对应该拥有一份Unicode标准。Unicode5.0还可以
Unicode与ISO10646之间的区别是什么?
Unicode协会公布的Unicode标准额外为某些字符定义了更多的语义。一般来说对于高质量印刷出版系统的实现者而言是更好的参考。Unicode详细说明了某些文字(如阿拉伯语)的表示形式的绘制算法,处理双向文本(如拉丁语和希伯来语的混合使用)的算法,排序和字符串比较所使用的算法以及其它更多内容。
另一方面,ISO10646标准,就像旧有的ISO8859(Latin-1)标准一样,不过是一个简单的字符集表。它指定了与标准相关的一些术语,定义了一些编码方法,并详细说明了如何实现UCS与其它已有的ISO标准的结合使用,例如ISO6429和ISO2022。还存在其它紧密相关的ISO标准,例如关于UCS字符串排序的
什么是UTF-8?
UCS和Unicode本质上只是将整型数值分配给字符的码表。而如何将一系列这样的字符或是单个字符的整型数值用字节串表示,则存在几种不同的方法。最显然的两种存储Unicode文本的编码方法是2或4字节序列组成的序列。这两种编码方法的正式名字分别是UCS-2和UCS-4。除非另外指定,每个字符对应的字节串遵循大端字节序(Big-endian)。一个ASCI或Latin-1文件只需要在每个ASCII字节前插入一个0X00字节,就可以转换为UCS-2文件;如果想转换为UCS-4文件,则需要在每个ASCII字节前插入三个0x00字节。在Unix世界中使用UCS-2(或UCS-4)会导致非常严重的问题。使用这两种编码方式的字符串,可能会包含诸如字节"/0"或"/"作为很多宽字符的组成部分,而这样的字节在文件名和其它C语言库函数中有特殊的意义。此外,大多数Unix工具都被设计用来处理ASCII文件的,不进行大幅度的修改是无法读取16-bit字符的。基于这些原因,在文件名、文本文件、环境变量等地方,UCS-2(或UCS-4)不是一种合适的Unicode外部编码。
在ISO10646-1:2000
UTF-8具有以下特性:
码值在U+0000至U+007F之间的UCS字符被简单的编码为0X00至0X7F(兼容ASCII)。这意味着只包含7-bitASCII字符的文件,在ASCII和UTF-8两种编码方法下具有相同的编码。
所有码值大于U+007的UCS字符都被编码为若干个字节组成的序列,序列中每个字节最高位均为1。因此,ASCII字节(0X00-0X7F)不可能作为任何其它字符的一部分而出现。
表示一个非ASCII字符的多字节序列中的第一个字节总是位于0XC0至0XFD之间,它能指出该字符由几个字节表示。多字节序列中的所有后续字节都位于0X80至0XBF。这使得重新同步变得容易,编码变得无状态化,对于字节的丢失具备健壮性。
所有可能的231个UCS字符都可被编码。
UTF-8编码的字符理论上最长可达6字节长,不过16-bit的BMP字符最多为3字节长。
保留了大端UCS-4字节串的排序顺序。
字节0XFE和0XFF在UTF-8编码中永远不会被用到。
以下字节序列用于表示一个字符。要使用的字节序列由字符的Unicode码值决定。
U-00000000–U-0000007F: | 0xxxxxxx |
U-00000080–U-000007FF: | 110xxxxx10xxxxxx |
U-00000800–U-0000FFFF: | 1110xxxx10xxxxxx10xxxxxx |
U-00010000–U-001FFFFF: | 11110xxx10xxxxxx10xxxxxx10xxxxxx |
U-00200000–U-03FFFFFF: | 111110xx10xxxxxx10xxxxxx10xxxxxx10xxxxxx |
U-04000000–U-7FFFFFFF: | 1111110x10xxxxxx10xxxxxx10xxxxxx10xxxxxx10xxxxxx |
例子:Unicode字符U+00A9=10101011(版权符号)在UTF-8方法中被编码为
1100001010101001=0XC20XA9
而字符U+2260=0010001001100000(不等于号)被编码为
111000101000100110100000=0XE20X890XA0
这种编码方法的正式名字和拼写是UTF-8,这里UTF代表UCSTransformationFormat。请勿在任何文档中用任何其它方式(如utf8或UTF_8)来表示UTF-8,当然除非你是指称一个变量名而不是这种编码本身。
针对UTF-8解码函数开发者的重要说明:基于安全的原因,UTF-8的解码函数不允许接受超出必要编码长度的UTF-8字节序列。例如,字符U+000A(换行符)只允许从UTF-8字节流中以0X0A的形式被接受(解码),而不是以下五种过长形式的任何一种:
0XC00X8A
0XE00X800X8A
0XF00X800X800X8A
0XF80X800X800X800X8A
0XFC0X800X800X800X800X8A
任何过长UTF-8序列都可能被滥用,以绕过UTF-8的子串测试——它只搜索最短的可能编码。所有过长UTF-8字节序列都以以下一种字节模式作为起始:
1100000x(10xxxxxx) |
11100000100xxxxx(10xxxxxx) |
111100001000xxxx(10xxxxxx10xxxxxx) |
1111100010000xxx(10xxxxxx10xxxxxx10xxxxxx) |
11111100100000xx(10xxxxxx10xxxxxx10xxxxxx10xxxxxx) |
MarkusKuhn的
谁发明的UTF-8?
如今被称为UTF-8的编码方式是由哪里可以找到优质的UTF-8示例文件?
以下是一些有趣的用于测试和示范的UTF-8示例文件:由Kermit项目提供的
MarkusKuhn的
存在哪些不同的编码方法?
UCS和Unicode标准两者本质上都是为每个字符分配对应码值的很大的表格。在使用"UCS","ISO16046"或"Unicode"这些术语时,所表示的只是字符和码值之间的映射关系,而并未涉及以何种方式将这些码值存储为字节串。ISO10646-1中定义了UCS-2和UCS-4两种编码方法。它们分别是一个字符对应2字节和4字节。ISO10646从最初就被设计成一个31-bit的字符集(码值在U+00000000至U+7FFFFFFF之间),然而直到2001才出现第一个不在BMP(即码表的前216个位置,参见ISO10646-2及
“Unicode”原本暗示着会采用UCS-2编码方法,并且最初在码表中并未为BMP之外的字符预留位置。然而,事实表明,为了支持某些特殊应用(古代文字,象形文字,数学和音乐排版等),必需的字符数超出了64K的容量。因此,Unicode被转变为一种21-bit的字符集,支持的码值范围为从U+00000000至U+0010FFFF。BMP为此定义了2x1024个特殊字符(U+D800至U+DFFF),称为代理字符(surrogatecharacter);把两个16-bit的代理字符连用,可以用来表示1024x1024个non-BMP字符。这样就产生了
除此之外,
编码名UCS-2,UCS-4,UTF-16,UTF-32并未表明所采取的字节序,然而根据ISO10646-1标准,除非另有约定,大端字节序为首选。在编码后面附加"BE"(大端,高位在前)和"LE"(小端,低位在前)来显示指定字节序,这已经成为惯例。
为了能实现自动检测字节序,在某些平台(特别是Win32)上,每个Unicode文件都以字符U+FEFF(零宽度空白符)作为起始,已经成了一种惯例;该字符又被称为字节序标记(Byte-OrderMark(BOM))。该字符的字节对换对等体U+FFFE是一个非法(未分配)的Unicode字符,因此它可以用来无二义性的区分UTF-16和UTF-32的大端和小端变种。
一个功能完备的编码转换器必须提供以下13种Unicode和UCS编码变种:
UCS-2,UCS-2BE,UCS-2LE,UCS-4,UCS-4LE,UCS-4BE,UTF-8,UTF-16,UTF-16BE,UTF-16LE,UTF-32,UTF-32BE,UTF-32LE
若未显式指明字节序,则采用执行转换操作的CPU的字节序,并在从输入流中每次读进字符U+FFFE时就改变当前字节序。USC-4和UTF-32,以及UCS-2和UTF-16的编码输出的区别在于越界字符的处理。处理不可表示字符的应急机制必须分别在UTF-32(码值大于U+0010FFFF)和UCS-2(码值大于U+FFFF)中被激活;而这些码值在USC-4或UTF-16中则会提供相应的表示。
一个优秀的编码转换器还应该提供选择性添加或移除BOM的特性:
无条件的为输出添加U+FEFF前缀
为输出添加U+FEFF前缀,除非该前缀已经存在
如果输出第一个字符为U+FEFF,则移除该字符
对于UTF-8,同样存在使用UTF-8编码后的BOM(字节串:0XFF0XBB0XBF)作为UTF-8文件起始标志的建议。然而基于以下原因,绝不应该在POSIX系统中采用这种做法:
在POSIX系统中,应该通过locale信息而不是文件类型魔数来确认纯文本文件的编码方法。混淆这两个概念会增加复杂性,并破坏已有功能。
在文件头部添加UTF-8签名会对很多已有惯例产生妨碍,例如内核在一个纯文本的可执行文件的起始处查找"#!",以定位合适的解释器。
对BOM的正确处理会为简单程序增加不必要的复杂性,例如cat或grep这样将多个文件的内容进行合并的程序。
除了规定可选的编码方法外,Unicode还规定了各种
NormalizationFormD(NFD):尽可能的将所有预制字符分裂成字符序列,例如使用U+0041U+0308(大写拉丁字母A,组合分音符号̈)而不是U+00C4(Ä)。此外,避免使用已废弃的字符;例如,使用U+0041U+030A(大写拉丁字母A,组合符号̊)而不是使用U+212B(物理符号埃:Å)。
NormalizationFormC(NFC):尽可能的使用预制字符而不是字符序列。例如是用U+00C4(Ä)而不是U+0041U+0308(大写拉丁字母A,组合分音符号̈),此外,避免使用已废弃的字符。例如,使用U+0041U+030A(大写拉丁字母A,组合符号̊)而不是使用U+212B(物理符号埃:Å)。对于Linux和WWW,NFC是首选范式。
NormalizationFormKD(NFKD):类似NFD,但还要避免使用兼容性字符(compatibilitycharacter),例如使用"fi"而不是U+FB01(LATINSMALLLIGATUREFI)。
NormalizationFormKC(NFKC):类似NFC,但还要避免使用兼容性字符(compatibilitycharacter),例如使用"fi"而不是U+FB01(LATINSMALLLIGATUREFI)。
一个功能完备的字符编码转换器还应该提供在不同范式之间进行的转换。在进行从NFKD到NFKC的映射时需要特别注意,因为这个过程中可能会出现语义信息的丢失,例如字符U+00B2(上标2)会被映射成2;而为了维持原有语义信息,可能需要添加额外的标记信息(例如,在HTML中为<SUP>2<SUP>)。
哪些编程语言支持Unicode?
大约在1993年之后出现的、更年轻的语言已经拥有专门用于Unicode/ISO10646-1字符的内置数据类型,这包括Ada95,Java,TCL,Perl,Python,C#及其它语言。ISOC90标准规定了处理多字节编码的机制以及宽字符。这些特性在
不幸的是,wchar_t在上世纪90年代被广泛应用于各种16-bit的亚洲编码之中,因此ISOC99标准被向后兼容这一问题所束缚,C无法利用类型wchar_t完美的支持UCS,如同Java和Ada95所做到的那样。然而,C编译器至少能够通知应用程序类型它所比保证的wchar_t在各种locale情况下可容纳的UCS值。为实现这一点,编译器会把宏__STDC_ISO_10646__定义为格式为yyyymmL的整型常量。格式中的年份和月份用于表明已实现的ISO/IEC10646及其修正的版本。例如,如果编译器实现了ISO/IEC10646-1:2000的话,__STDC_ISO_10646__==200009L,
Linux下应该如何应用Unicode?
在UTF-8出现前,全世界所有的Linux用户不得不使用各自语言特定的ASCII扩展。最流行的有欧洲范围使用的ISO8859-1和ISO8859-2,希腊使用的ISO8859-7,俄罗斯使用的KOI-8/ISO8859-5/CP1251,日本使用的EUCandShift-JIS,以及中国台湾省使用的由于存在这些困难,多数Linux发行版和开发者正逐步淘汰这些遗留编码而改用UTF-8。过去几年内Linux世界对UTF-8的支持有了极大改进,现在UTF-8的使用已融入日常操作,如:
文本文件(源代码,HMTL文件,e-mail等等)
文件名
标准输入、标准输出和管道
环境变量
剪切和粘贴操作的缓存
telnet,modem和与终端模拟器间的串口通讯
以及任何其它字节序列原本被解释为ASCII的应用场合。
在UTF-8模式下,终端模拟器,例如xterm或Linux控制台驱动,将每次键盘敲击都转换为对应的UTF-8字节序列,并将它送至后台应用程序的标准输入。类似的,应用程序所有在标准输出上的输出都被送至终端模拟器,在这里由UTF-8解码器处理后,以16-bit字体显示在屏幕上。
对Unicode的各种高级功能(例如,阿拉伯文和印度文的高品质印刷)的完备支持,只有复杂的多语种字处理软件包才会提供。Linux现今对Unicode广泛提供的支持要比这简单许多,并且其主要目的是取代旧式的8位和16位字符集。Linux终端模拟器和命令行工具通常只提级别1的ISO10646-1(未定义组合字符)实现,只支持拉丁语、希腊语、亚美尼亚语、格鲁吉亚语、CJK以及不需要额外处理支持的大量科学符号。处于这个实现级别的UCS与ISO8859有很大的相似性,唯一的重大差别在于UCS提供了数以千记的字符,每个字符可以表示为多字节序列,还有表意的汉字/日文/韩文/字符在终端上显示时会占据两个字符位置(双倍宽度)。
UCS级别2实现(对某些特定语言,特别是
一家很有影响力的非POSIXPC操作系统厂商(这里我们略掉它的名字)曾建议所有Unicode文件都以字符U+FEFF(零宽度空白符)作为起始;该字符又被称为“签名”或“字节序标记(BOM)",用于判断文件使用的编码方式和字节序。Linux/Unix不使用任何BOM或签名。那样会破坏太多现有的基于ASCII的文法惯例(例如脚本以#!开头)。在POSIX系统中,系统选定的locale就已经表明了程序所有的输入和输出文件预期编码方式。不带签名的UTF-8也被建议称为"UTF-8N"文件,但这个非标准称谓在POSIX世界中很少被使用。
在转向使用UTF-8工作之前,先将你的系统更新为较新的具备UTF-8支持的发行版。如果你使用的发行版比SuSE9.1或RedHat8.0,这一点尤为重要。在这些版本之前的发行版对UTF-8的支持还没有成熟到可推荐用于日常使用的程序。
开发者该如何修改代码?
如果你是一名开发者,可通过若干种方法为程序添加UTF-8支持。可以将它们分为两类,这里我分别称之为软转换和硬转换。软转换中,数据在任何时候都以UTF-8格式被保存,只需要对代码进行很少的改动。硬转换中,程序读入的任何数据都被转换并存放在宽字符数组中,并且在程序内部都以这种形式存在;字符串只有在被输出时才转换为UTF-8编码格式,在程序内部,所有字符都由固定长度的内存对象进行表示。我们还可以根据程序处理字符串时对标准库函数的依赖程度,将对UTF-8的支持区分为硬编码和locale相关两种。C语言提供了很多字符串处理函数,它们被设计成可以处理任何locale相关的多字节编码。一个完全依赖这些库函数的程序员,无需了解UTF-8编码的实际细节。很可能只需适当改变locale设置,就能使程序自动的也支持其它几种多字节编码(例如EUC)。程序员可选用的另一种方式,是在代码中根据自己对UTF-8格式的了解,进行硬编码。在某些情况下这种方法能获得显著的性能提升,对于只用来处理ASCII和UTF-8编码的程序,也许是最好的选择。
即使是在要求由libc对每种编码方式提供支持的情况下,为UTF-8格式添加额外的优化代码也是值得的。多亏了UTF-8的自同步特性,对它的处理可以很有效率的完成。locale依赖的libc字符串函数可能比对应的硬编码函数速度慢上两个数量级。GNUgrep就是一个反面例子;它完全依赖于locate相关的libc函数,如mbrlen()来提供通用多字节编码支持。这使得在多字节模式下的速度比单字节模式下要慢100倍!而其它依靠硬编码提供UTF-8支持的正则表达式,例如Perl5.8,则不会出现这样剧烈的性能下降。
大多数应用程序仅用软转换就可以很好的工作于UTF-8环境。正是这一点使得在Unix中引入UTF-8具有可行性。举两个简单的例子,cat和echo这样的程序不需作任何修改就能在UTF-8环境下工作。它们完全不受输入和输出编码方式的影响,无论是ISO8859-2还是UTF-8,因为它们的工作基于字节流而不对其进行其它处理。它们只认识ASCII字符和'/n'这样的控制字符——而这些字符的编码在UTF-8中不发生任何改变。因此,对于这些应用程序,UTF-8的编码和解码工作完全交由终端模拟器为之完成。
而任何程序,如果是通过统计字节数来确定字符数目,则需要对代码进行一些修改。类似于其它多字节编码方式,在涉及到UTF-8文本字符串的长度问题时,程序员必须明确区分下面三个概念:
字节数
字符数
显示宽度(例如,在VT100终端模拟器中显示时所占据的占位符数量)
C函数strlen()总是返回字符串在内存中占据的字节数。若strlen()被用于该目的而被调用,不需要进行改动。
C语言中使用mbstowcs(NULL,s,0)来统计字符串中的字符数,该函数具有良好的可移植性。只要设定好合适的locale,该函数在UTF-8环境下就能正常工作。一种计算UTF-8编码的字符串中字符数的硬编码方法,是统计所有值不在0X80-0XBF之内的字节数,因为它们只是字节而不是独立的字符。然而,需要计算字符数的应用场景是惊人的罕见的。
在基于ASCII或ISO8859的程序中,strlen()更常见的用途是预计字符串被打印到终端时,光标移动的列数。在UTF-8环境下,无论是字节数或是字符数都无法用来预计字符串的显示宽度,因为表意字符(汉字/日文/韩文)会占据两列的位置,而控制字符和组合字符不占位置。为了确定一个字符串在终端屏幕显示时的宽度,需要对UTF-8字节串解码后,调用wcwidth()函数来测量每个字符的显示宽度,或调用wcswidth()来测量整个字符串的显示宽度。
举例来说,ls这个程序(要支持UTF-8)必须修改代码,因为如果不知道文件名的显示宽度,它就无法以格式化的表格形式向用户呈现目录结构。类似的,所有假设输出由定宽字体表示并据此完成格式化的程序,都必须在UTF-8模式下重新学习如何计算显示宽度。编辑功能,例如删除一个字符这样的操作,必须稍作修改以删除该字符对应的所有字节。受此影响的包括编辑器(例如vi,emacs,realine等)以及使用了ncurses库的程序。
所有类Unix内核都可以很好的应用软转换,只需进行很细微的修改就可以完全支持UTF-8。
控制台显示和键盘驱动(还有VT100模拟器)必须具备UTF-8编码和解码功能,并且至少要支持Unicode字符集的某个子集。Linux自从内核1.2版本后就已支持这些特性(发送ESC%G至控制台来激活UTF-8模式)。
外部文件系统的驱动程序,例如VFAT和WinNT,必须完成文件名编码方式的转换。UTF-8是可用转换选项之一,而mount命令必须告知内核驱动:用户进程希望看到UTF-8编码的文件名。由于VFAT和WinNT已经使用Unicode编码,UTF-8是唯一可选的保证无损转换的编码方式。
所有POSIX系统的tty驱动都支持一种"cooked“模式,该模式提供某些原始的行编辑功能。为了保证字符删除功能(charatcer-erase)在UTF-8下正常工作,需要通过某种方式告知tty驱动在"cooked“模式下不要将值在0x80-0XBF的跟随字节视为字符,而应将它们视为一个UTF-8多字节序列的一部分而删除。由于内核无视libc提供的lcoale机制,需要另一种机制来告知tty驱动来使用UTF-8。Linux内核2.6及之后的版本在类型为termios结构的成员变量c_iflag中支持一个IUTF8位。在其被置位时,"cooked“模式的行编辑器会正确的处理UTF-8多字节序列。可在shell中通过命令"sttyiutf8“来进入该模式。Xterm及类似程序在UTF-8环境中被调用时,应该自动将该位置位。
C语言对Unicode和UTF-8的支持
从GNUglibc2.2开始,类型wchar_t只用于存放32-bit的ISO10646码值,而独立于当前使用的locale。这一点通过对宏__STDC_ISO_10646__的定义来通知应用程序,如ISOC99所要求的一般。glibc2.2或更高版本完全实现了ISOC的多字节转换函数(mbsrtowcs(),wcsrtombs()等),可以用来在wchar_t和任意locale相关的多字节编码进行转换,包括UTF-8,ISO8859-1等。例如,你可以编写如下的程序
#include<stdio.h>
#include<locale.h>
intmain()
{
if(!setlocale(LC_CTYPE,"")){
fprintf(stderr,"Can'tsetthespecifiedlocale!"
"CheckLANG,LC_CTYPE,LC_ALL./n");
return1;
}
printf("%ls/n",L"SchöneGrüße");
return0;
}
将locale设置为LANG=de_DE并调用该程序,则程序输出为ISO8859-1格式。而将locale设置为LANG=de_DE.UTF-8,则程序输出为UTF-8。printf函数中的%ls格式说明符会(隐式的)调用wcsrtombs函数来将参数中的宽字符字符串转换为locale特定的多字节编码格式。
C的很多字符串函数都是locale无关的,它们所处理的只是以'/0'为结束符的字节串,例如:
strcpystrncpystrcatstrncatstrcmpstrncmpstrdupstrchrstrrchr strcspnstrspnstrpbrkstrstrstrtok
这其中的某些函数,如strcpy,既可以用于单字节字符集(如ISO8859-1),也可以用于多字节编码(如UTF-8)的字符集,因为它们完成的任务无需了解一个字符究竟对应几个字节,而其它函数(如strchr)所完成的任务则依赖于一个字符编码成一个字节,因此对于UTF-8用处不大(如果在UTF-8字符串中寻找ASCII字符的话,strchr依然能正常工作)。
其它的C函数是locale相关的,在UTF-8locale下同样可以正常工作,如:
strcollstrxfrm
UTF-8模式该如何被激活?
假设你的程序属于软转换类型,没有调用任何C的locale相关的多字节函数(mbsrtowcs(),wcsrtombs()等)来将所有字符转换为wchar_t以用于后面的处理,有时候却必须以某种方式判明:它所处理的文本数据,是假定为某种8-bit编码(例如ISO8859-1,一个字节=一个字符)还是UTF-8编码。只要所有人都只使用UTF-8,当然可以直接设为默认方式,但是现在必须同时支持UTF-8和传统的8-bit字符集。最早提供UTF-8支持的一批应用程序,使用了各种不同的命令行开关来激活各自的UTF-8模式,例如著名的xterm-u8。事实证明这是一个糟糕的主意。记住每一个应用程序的命令行选项或其他配置方法是非常单调乏味的,,因此命令行选项并不是激活UTF-8模式的正确途径。
激活UTF-8模式的正确途径是利用POSIX的locale机制。locale是包含特定文化中应用惯例的信息集合,包括字符编码、日期/时间符号、字母排序规则、测量体系以及普通办公纸张尺寸等。locale的名称通常由分别在
命令“locale-a”可用来获取包含系统已安装的所有locale(通常位于/usr/lib/locale/)的列表。将环境变量LANG设置为要选用的locale的名称。当C程序执行setlocale(LC_CTYPE,"")函数时,库函数会依次检查环境变量LC_ALL、LC_CTYPE和LANG,三者中的首个非空值将决定为分类LC_TYPE(它可以控制多字节转换函数)加载哪种locale。locale配置被分成不同类别。例如,LC_TYPE定义字符编码,而LC_COLLATE定义字符串排序顺序。环境变量LANG为所有类别设置了默认值,不过LC_*变量可用于覆盖单个类别的设置。没有必要过分在意locale中的国家标识符。en_GB(EnglishinGreatBritain)和en_AU(EnglishinAustralia)这两个locale通常只在分类LC_MONETARY(货币名称,货币数额的打印规则)上存在差别,而几乎没有Linux应用程序会用到这些。LC_CTYPE=en_GB和LC_CTYPE=en_AU具有完全相同的效果。
命令“localecharmap”可用来查看当前locale的字符编码。如果你为LC_CTYPE选择了一个UTF-8locale的话,该命令的执行结果会是"UFT-8"。命令"locale-m“则返回包含所有已安装编码的列表。
如果开发者完全使用C提供的多字节函数来完成外部字符编码和程序内部使用的wchar_t之间的转换的话,那么C的库函数则负责根据LC_CTYPE的值选择合适的编码,这样应用程序甚至不必明确知道使用当前的多字节编码。
然而,如果你不希望所有的工作都依赖于库中的多字节函数(例如,因为你认为这样需要对程序进行多处修改或是效率不高),那么你的程序必须靠自己来找出何时激活UTF-8模式。为了做到这一点,在任意X/Open兼容系统中,头文件<langinfo.h>可用的情况下,可以编写类似下面的代码来检测是否当前locale使用UTF-8编码:
utf8_mode=(strcmp(nl_langinfo(CODESET),"UTF-8")==0);
当然你首先必须在程序的起始处添加上"setlocale(LC_CTYPE,"")"来根据环境变量设置locale。命令"localecharmap“同样是调用nl_langinfo(CODESET),来为用户查出当前locale所使用的编码名称的。该函数在几乎所有现代Unix系统上都可用。FreeBSD4.6版(2002-06)之后添加了对nl_langinfo(CODESET)的支持。如果你需要测试nl_langinfo(CODESET)可用性的autoconf的话,这里有BrunoHaible推荐的一个:
========================m4/codeset.m4================================
#serialAM1
dnlFromBrunoHaible.
AC_DEFUN([AM_LANGINFO_CODESET],
[
AC_CACHE_CHECK([fornl_langinfoandCODESET],am_cv_langinfo_codeset,
[AC_TRY_LINK([#include<langinfo.h>],
[char*cs=nl_langinfo(CODESET);],
am_cv_langinfo_codeset=yes,
am_cv_langinfo_codeset=no)
])
iftest$am_cv_langinfo_codeset=yes;then
AC_DEFINE(HAVE_LANGINFO_CODESET,1,
[Defineifyouhave<langinfo.h>andnl_langinfo(CODESET).])
fi
])
=======================================================================
[你也可以不通过调用setlocale(),而是尝试手动查询环境变量。按照LC、ALL,LC_CTYPE,LANG的顺序,从中寻找第一个值非空的环境变量;当该值中包含“UTF-8”这个子字符串时,将UTF-8模式设为默认值(不过还是可以被命令行开关覆盖),因为这已经可靠合理的表示者C库函数已被要求使用一个UTF-8Locale。以下是一段实例代码:
char*s;
intutf8_mode=0;
if(((s=getenv("LC_ALL"))&&*s)||
((s=getenv("LC_CTYPE"))&&*s)||
((s=getenv("LANG"))&&*s)){
if(strstr(s,"UTF-8"))
utf8_mode=1;
}
当然这种方法依赖于所有UTF-8locale的名称中都包含编码名称,而事实并非总是如此,因此使用nl_langinfo()很明显是更好的选择。如果你真的担心使用nl_langinfo()缺少足够的可移植性,对于不提供nl_langinfo(CODESET)的系统还有MarkusKuhn编写的
如何获得UTF-8版本的xterm?
LC_CTYPE=en_GB.UTF-8xterm/
-fn'-Misc-Fixed-Medium-R-SemiCondensed--13-120-75-75-C-60-ISO10646-1'
在新开启的xterm中对某些示例文件,例如
如果你的XFree86版本低于4.0,那么可以单独下载最新开发版本的Xterm,并且自己通过“./configure--enable-wide-chars;make”或者“xmkmf;makeMakefiles;make;makeinstall;makeinstall.man”来完成编译。
如果你没有支持UTF-8的locale,那么启动xterm时加上命令行选项-u8,来将其输入和输出切换至UTF-8
xterm对Unicode的支持如何?
XFree864.0.1中的Xterm只在定宽字体和从左至右的书写顺序的条件西支持ISO10646-1的实现级别1(无组合字符)。换句话说,除了能解码UTF-8字节序列以及使用16-bit字符外,终端语义与ISO8859-1基本保持一致。而到了XFree4.0.3,两项重要的功能被添加进来:
对于CJK表意文字,自动转换至双倍宽度的字体
对组合字符的简单加粗:
如果选择的正常字体的尺寸为X×Y像素,那么xterm会尝试额外加载一个尺寸为2X×Y的字体;除了具有双倍的平均宽度属性之外,它与前者具有相同的XLFD(Xl逻辑字体描述)。xterm将用这个字体来显示所有Unicode技术报告#11中确定的被赋予EastAsianWide(W)orEastAsianFullWidth(F)属性的Unicode字符。
XFree864.x中附带的以下字体适用于在终端模拟器和编辑器中显式日文和韩文Unicode字符:
6x13-Misc-Fixed-Medium-R-SemiCondensed--13-120-75-75-C-60-ISO10646-1
6x13B-Misc-Fixed-Bold-R-SemiCondensed--13-120-75-75-C-60-ISO10646-1
6x13O-Misc-Fixed-Medium-O-SemiCondensed--13-120-75-75-C-60-ISO10646-1
12x13ja-Misc-Fixed-Medium-R-Normal-ja-13-120-75-75-C-120-ISO10646-1
9x18-Misc-Fixed-Medium-R-Normal--18-120-100-100-C-90-ISO10646-1
9x18B-Misc-Fixed-Bold-R-Normal--18-120-100-100-C-90-ISO10646-1
18x18ja-Misc-Fixed-Medium-R-Normal-ja-18-120-100-100-C-180-ISO10646-1
18x18ko-Misc-Fixed-Medium-R-Normal-ko-18-120-100-100-C-180-ISO10646-1
XFree86已为环绕型组合字符(nonspacingorenclosingcombiningcharacters,即那些在
XFree864.x中附带的下列字体适用于显示Latin等组合字符。其它字体只适用于附加于小号字符的组合重音字符。
6x12-Misc-Fixed-Medium-R-Semicondensed--12-110-75-75-C-60-ISO10646-1
9x18-Misc-Fixed-Medium-R-Normal--18-120-100-100-C-90-ISO10646-1
9x18B-Misc-Fixed-Bold-R-Normal--18-120-100-100-C-90-ISO10646-1
XFree864.x中附带的下列字体适用于显示泰语中的组合字符:
6x13-Misc-Fixed-Medium-R-SemiCondensed--13-120-75-75-C-60-ISO10646-1
9x15-Misc-Fixed-Medium-R-Normal--15-140-75-75-C-90-ISO10646-1
9x15B-Misc-Fixed-Bold-R-Normal--15-140-75-75-C-90-ISO10646-1
10x20-Misc-Fixed-Medium-R-Normal--20-200-75-75-C-100-ISO10646-1
9x18-Misc-Fixed-Medium-R-Normal--18-120-100-100-C-90-ISO10646-1
字体
文本模式应用程序的开发者需要注意:
由于有了对CJK表意文字和组合字符的支持,xterm的输出特性有些更类似于比例字体,因为一个拉丁/希腊/斯拉夫(等)字符占一个光标位置,CJK字符占2个,组合字符占0个。OpenGroup的
#include<wchar.h>
intwcwidth(wchar_twc);
intwcswidth(constwchar_t*pwcs,size_tn);
在C的库函数未提供合适功能的平台上,应用程序可以免费使用
在可以预见的未来一段时间内,Xterm可能不会支持以下这些更复杂的、完善的Unicode表现引擎所提供的特性:
希伯来文和阿拉伯文的双向输出
阿拉伯语的替换定义表
substitutionof
组合字符的任意堆积
因此,希伯来和阿拉伯用户使用的程序,在将字符串输出到终端前,必须将字符串反向并保证右对齐。换言之,双向处理必须由程序而不是xterm完成。与ISO8859相比,希伯来文和阿拉伯文所面临的处境,至少以提供预制字符和表现表(presentationform)的形式得到了改善。现阶段来看,难以预测xterm中是否会加入双向支持以及它将具体如何工作。
如果你打算在程序中支持双向文本输出,可以参考DovGrobgeld的
Xterm目前不支持阿拉伯、叙利亚或梵文的文本格式化算法,尽管RobertBrady已经为此发布了一些
从哪里可以找到ISO10646-1x11字体?
在过于几个月中已经有很多Unicode字体在X11中变得可用,而且该名单正在迅速增长。MarkusKuhn同很多其他志愿者一起将X11附带的旧有的-misc-fixed-*-iso8859-1字体扩展为涵盖所有欧洲字符(拉丁语、希腊语、斯拉夫语、注音符号、数学和技术符号,某些字体中甚至包含亚美尼亚语、格鲁吉亚语、片假名、泰语以及更多)的字体表。更多信息请参考
Markus还制作了X11R6.4中的所有Abobe和B&HBDF字体的
XFree864.0附带了一个集成的
未来的XFree86分发版本很有可能去除大多数旧式的BDF字体,而替换成ISO10646-1编码的版本。X服务器则会增加一个自动编码转换器;当旧式的8位软件需要使用旧式字体编码时,它会根据其ISO10646-1版本的字体文件对用户透明的、动态的创建ISO8859-*字体编码。现代软件应该优先直接使用ISO10646-1字体编码。
日本的
DmitryYu.Bolkhovityanov提供了一个BDF格式的
RomanCzyborra的
GeorgeWilliams提供了
BirgerLangkjer为Linux提供了一个
AlanWood给出了支持不同Unicode值域的
UnicodeX11字体名字以-ISO10646-1结尾,该值已成为所有Unicode和ISO10646-116-bit字体对应的
*-ISO10646-1字体通常也指定了一个DEFAULT_CHAR值,指向一个特殊的非Unicode字形,用于表示该字体不支持的字符(通常是一个虚线框,一个H的大小,定位于0x00)。这保证用户至少能意识到此处有一个不被支持的字符。xterm所使用的较小的定宽字体如6x13等,永远不可能涵盖所有Unicode字符,因为很多语言,例如日文,只有在字体的像素尺寸与欧洲用户广泛使用的大很多的情况下才能被表示。用于欧洲字符的典型Unicode字体只包含1000至3000的字符子集,例如
你可能注意到,
与UTF-8终端模拟器相关的问题有哪些?
VT100g终端模拟器可接受ISO2022(=从ISO2022的角度看,UTF-8是“另一套编码系统"(参见ECMA35的15.4节)。UTF-8位于ISO2022SS2/SS3/G0/G1/G2/G3的世界之外,因此如果从IS02022切换至UTF-8的话,所有的SS2/SS3/G0/G1/G2/G3状态信息都变得无意义,直到离开UTF-8模式并切换回ISO2022为止。UTF-8是一种无状态编码,即一个自结束的字节序列就完全确定了要表示的字符,而不用依赖任何转换状态。ISO10646-1中的G0和G1与ISO8859-1中的一样,而G2/G3在ISO10646-1中并不存在,因为每个字符在码表中的位置固定,切换不会发生。在UTF-8模式下,终端不会因为载入某个二进制文件而意外的切换至奇怪的图形字符模式。这使得一个终端在UTF-8模式下比在ISO2022模式下具备好得多的健壮性,因此能将终端锁定于UTF-8模式而不会意外切换回ISO2022的话,是会很有用的。
ISO2022标准规定了一系列的ESC%序列,用于离开ISO2022模式而切换至其它编码系统,很多这样的用于UTF-8的序列已经被注册进入
ESC%G,从ISO2022环境下激活一个UTF-8模式(对实现级别无要求),并允许再次返回ISO2022模式。
ESC%@,在UTF-8模式下输入该序列的话,则从UTF-8模式返回IS02022模式。
ESC%/G,切换至实现级别1的UTF-8模式,不允许返回。
ESC%/H,切换至实现级别二的UTF-8模式,不允许返回。
ESC%/I,切换至实现级别三的UTF-8模式,不允许返回。
终端模拟器处于UTF-8模式下时,输入的任何ISO2022转义序列例如G2/G3切换等都被其忽略。终端模拟器唯一会处理的ISO2022转义序列是ESC%@,用于从UTF-8返回ISO2022。
UTF-8仍然允许你使用像CSI这样的C1控制字符,尽管UTF-8也使用0X80-0X9F之间的字节。重要的是要明白,UTF-8模式下的终端模拟器在解释执行任何控制字符前,必须先对输入的字节流进行UTF-8解码。C1字符同其它大于U+007F的字符一样要先被解码。
很多现在可用的文本模式应用程序,它们期望于与使用旧式编码的终端进行交互,或者使用ISO2022转义序列用于切换终端字体。为了使这些应用程序在UTF-8终端模拟器中可用,可能需要使用一个执行ISO2022和UTF-8之间动态转换的中间转换层,例如JuliuszChroboczek发布的
支持UTF-8的应用程序有哪些?
警告:自2003年年中开始,这一部分日趋变得不完备。支持UTF-8,已成为大多数维护良好的软件包的标准特性。这份清单很快就会被转变为最流行的存在UTF-8相关问题的软件清单。终端模拟器和通讯
随XFree864.0或更高版本分发的xterm在UTF-8locale下能工作,条件是用户要选择一种*-iso10646-1字体。尝试运行一下"LC_CTYPE=en_GB.UTF-8xterm-fn'-Misc-Fixed-Medium-R-Normal--18-120-100-100-C-90-ISO10646-1'"。[新增内容]JuliuszChroboczek’s提供的
编辑和字处理
GNU
[新加内容]
编程
邮件和互联网
邮件客户端大多数现代web浏览器,如
打印
MarkusKuhn’s编写的
MarkusKuhn编写的
SergeWinitzki编写的
其它
第一批Unix下可用的UTF-8工具源自于
表格工具
用于改善UTF-8支持的补丁有哪些?
在各主要发行版中已经包含了很多这样的补丁。OpenI18N(以前的Li18nux)项目下的高级工具开发小组已为诸如cut,fold,glibc,sed,uniq,xterm等各种工具提供了可改善UTF-8支持的国际化补丁程序。
BrunoHaible编写的
BrunoHaible还为stty,linux内核tty等制作了各种
文本模式web浏览器
有没有用于处理Unicode的免费库?
UlrichDrepper发布的X.Net提供的
BrunoHaible提供的字符集转换库
BrunoHaible编写的
MarkusKuhn提供了
MarkusKuhn编写的
各种Xwidget库对于Unicode支持的现状如何?
Jean-MarcLienher为
哪些支持UTF-8的软件包正处于开发阶段?
Emacs23计划内部完全使用UTF-8编码。如果你有兴趣参与或测试的话,请加入邮件列表emacs-devel@gnu.org。Solaris对UTF-8的支持如何?
Solaris从版本2.8开始就至少部分支持UTF-8。要使用UTF-8模式,只需设定好某个UTF-8locale,例如在Cshell中输入:setenvLANGen_US.UTF-8
这样,终端模拟器dtterm就可用来输入和输出UTF-8文本,打印过滤器mp就能在PostScript打印机上打印UTF-8文件。目前,localeen_US.UTF-8被Motif和CDE桌面应用程序和库所支持,但并不被OpenWindows,XView和OPENLOOK桌面应用程序和库所支持。
更多信息请参考SUN的
UTF-8可以应用于Web吗?
是的。HTTP服务器可以通过两种方式告知客户某个文档采用了UTF-8编码:保证传输该文档的HTTP头部中包含如下内容:
Content-Type:text/html;charset=utf-8
这适用于HTML文件;对于纯文本文件,则要变为
Content-Type:text/plain;charset=utf-8
如何实现上述要求取决于你使用的Web服务器。如果使用Apache并且有一个子目录,其中存放的素有.html或.txt文件都采用UTF-8编码的话,在该目录下创建一个.htaccess文件并在其中添加以下两行:
Addtypetext/html;charset=UTF-8html
Addtypetext/plain;charset=UTF-8txt
网站管理员可以修改/etc/httpd/mime.types,对所有子目录同时完成相同的修改。
如果你无法影响Web服务器如何自动在文档之前添加HTTP头部的话,那么就在HTML文档的HEAD元素中添加
<METAhttp-equiv=Content-Typecontent="text/html;charset=UTF-8">
这样就能产生相同的效果。很显然这种方法只对HTML文件有效,而无法应用于纯文本文件;而且,该方式只能在解析器已经开始读取文件内容后才能告知解析器该文件使用的编码格式。因此,相比之下,该方式显然不如前者优雅。
目前最广泛使用的浏览器对UTF-8的有足够好的支持,因而通常推荐在网页中采用UTF-8编码。陈旧的Netscape4浏览器使用了一种犯人的单一大号字体来显示所有UTF-8文档。最好升级到Mozilla,Netscape或其它较新的浏览器(Netscape4总体来看有很多bug而且已停止了维护)。
还有一个问题,HTML表单中输入的非ASCII字符在随后的将内容发送给服务器某个CGI脚本的GET或POST请求中该如何编码?不幸的是无论在标准还是实现领域,正如AlanFlavell在
PostScript的字形名字与UCS码值是怎么关联在一起的?
参考Adobe的有没有已制定好的Unicode子集?
对包含40000个以上字符的Unicode进行完整和全面的实现,这是一个庞大的工程。然而,很多情况下(尤其对于欧洲市场)同之前一样只实现几百或几千字符就足够了,而且仍然享受Unicode所提供的的单一简单编码覆盖所有需要字符的简洁性。已经有很多不同的UCS子集被制定出来:欧洲标准委员会CEN/TC304在CWA13873中定义了UCS的三个欧洲子集:
MES-1是一个很小的含335个字符的(Unicode的)拉丁语子集。它恰好包含了ISO6937中的所有字符再加上欧元符号。这意味着MES-1包含了ISO8859part1,2,3,4,9,1,15中的全部字符[注意,如果目的仅是提供最简单、最低成本的合理UCS中欧子集,我会选择实现MES-1外加以下14个在Windows码值页1252之中却不在MES-1之中的字符:U+0192,U+02C6,U+02DC,U+2013,U+2014,U+201A,U+201E,U+2020,U+2021,U+2022,U+2026,U+2030,U+2039,U+203A]。
MES-2是一个包含1052个拉丁语/希腊语/叙利亚语/亚美尼亚语/格鲁吉亚语字符的子集。它涵盖了欧洲(不止是欧盟!)和欧洲语言国家使用的所有语言和所有8-bit码值页。它还附加了一个在技术文档中使用的数学符号的很小集合。MES-2是MES-1的超集。如果只面向欧洲或西方市场进行开发,MES-2是推荐使用的字符表。[注意:由于奇怪的政治原因,下面8个WGL4ZI字符不在MES-2之中:U+2113,U+212E,U+2215,U+25A1,U+25AA,U+25AB,U+25CF,U+25E6。如果你要实现MES-2,绝对应该附加支持这8个字符,这样就能附加实现对WGL4的兼容]。
MES-3是一个包含2819个字符、非常全面的UCS子集。它将所有对欧洲用户有潜在使用可能的UCS字符包含进来。它是为更有野心的实现者提供的。MES-3是MES-2和WGL4的超集。
JISX0221-1995为日本用户定义了7个不相交的UCS子集:
BasicJapanese(6884characters):JISX0208-1997,JISX0201-1997
JapaneseNon-ideographicSupplement(1913characters):JISX0212-1990non-kanji,plusvariousothernon-kanji
JapaneseIdeographicSupplement1(918characters):someJISX0212-1990kanji
JapaneseIdeographicSupplement2(4883characters):remainingJISX0212-1990kanji
JapaneseIdeographicSupplement3(8745characters):remainingChinesecharacters
Full-widthAlphanumeric(94characters):forcompatibility
Half-widthKatakana(63characters):forcompatibility
ISO10646标准将其字符表分成若干的
MarkusKuhn在
MarkusKuhn编写的Perl脚本
在不同编码方式之间进行转换时需要考虑哪些问题?
Unicode协会维护了一组Unicode和其它旧有编码标准之间的Unicode映射表只在一定程度上适用于将文本直接由旧式编码转换为Unicode。然而高端的转换工具应该提供交互机制,使得在旧式编码中一致而在Unicode中被区分的字符可以被逐个的交互式或半自动的消除歧义。
从Unicode到旧式字符集的反向转换需要以上映射表的多对一扩展。在很多旧式编码中,若干Unicode字符必须被映射至单一码值。Unicode协会目前并未为此而维护标准多对一表,而且也没有为字符集转换工具定义任何标准行为。
以下是一些从Unicode转为其它形式时必须解决的多对一映射的例子:
UCScharacters | equivalentcharacter | intargetcode |
U+00B5MICROSIGN U+03BCGREEKSMALLLETTERMU | 0xB5 | ISO8859-1 |
U+00C5LATINCAPITALLETTERAWITHRINGABOVE U+212BANGSTROMSIGN | 0xC5 | ISO8859-1 |
U+03B2GREEKCAPITALLETTERBETA U+00DFLATINSMALLLETTERSHARPS | 0xE1 | CP437 |
U+03A9GREEKCAPITALLETTEROMEGA U+2126OHMSIGN | 0xEA | CP437 |
U+03B5GREEKSMALLLETTEREPSILON U+2208ELEMENTOF | 0xEE | CP437 |
U+005CREVERSESOLIDUS U+FF3CFULLWIDTHREVERSESOLIDUS | 0x2140 | JISX0208 |
Unicode协会曾维护了至CJK字符集的映射表,但已经宣布它们是过时和废弃的,这事因为它们在UnicodeWeb服务器上的存在而导致了一大批幼稚、不完整的转换器被开发出来。要特别指出的是,现已废弃的CJKUnicode映射表,在某些情况下必须进行轻微的修改来保留组合(字符)编码中的信息(完整性)。例如,标准映射表为ASCII-Unicode-ASCII,以及JISX0208-Unicode-JISX0208的转换链提供了roung-trip兼容性。然而,EUC-JP编码同时涵盖了ASCII和JISX0208,而ASCII映射表和JISX0208映射表在一个字符上存在重叠,即U+005C(反向分割符'/')。因此EUC-JP转换器必须使用一个略微修改过的JISX0208映射表,使得JISX0208中的0X2140(对应EUC-JP中的0XA10XC0)被映射到字符U+FF3C(全宽度反向分割符)。。这样就保证从EUC-JP到Unicode再到EUC-JP的过程中不会出现信息的丢失,即round-trip兼容性。
除了使用标准规范映射表外,编码转换器的开发者还可以提供对转译(transliteration)的支持。转译的意思一个将Unicode字符在目标编码中转换为一个在图形和(或)语义上相似的字符,即使这两者规范化(normalization)后在Unicode中是不同的字符。例子如下:
UCScharacters | equivalentcharacter | intargetcode |
U+0022QUOTATIONMARK U+201CLEFTDOUBLEQUOTATIONMARK U+201DRIGHTDOUBLEQUOTATIONMARK U+201EDOUBLELOW-9QUOTATIONMARK U+201FDOUBLEHIGH-REVERSED-9QUOTATIONMARK | 0x22 | ISO8859-1 |
X11对使用Unicode已经准备就绪了么?
这其中已经得到修正的包括:
Keysyms:自X11R6.9之后,