浅谈文字编码和Unicode(下)

xiaoxiao2021-03-01  8

浅谈文字编码和Unicode(下)

3 字符编码模型

程序员经常会面对复杂的问题,而降低复杂性的最简单的方法就是分而治之。Peter Constable在他的文章"Character set encoding basics Understanding character set encodings and legacy encodings"中描述了字符编码的四层模型。我觉得这种说法确实可以更清晰地展现字符编码中发生的事情,所以在这里也介绍一下。

3.1 字符的范围(Abstract character repertoire)

设计字符编码的第一层就是确定字符的范围,即要支持哪些字符。有些编码方案的字符范围是固定的,例如ASCII、ISO 8859 系列。有些编码方案的字符范围是开放的,例如Unicode的字符范围就是世界上所有的字符。

3.2 用数字表示字符(Coded character set)

设计字符编码的第二层是将字符和数字对应起来。可以将这个层次理解成数学家(即从数学角度)看到的字符编码。数学家看到的字符编码是一个正整数。例如在Unicode中:汉字“字”对应的数字是23383。汉字“”对应的数字是134192。

在写html文件时,可以通过输入"字"来插入字符“字”。不过在设计字符编码时,我们还是习惯用16进制表示数字。即将23383写成0x5BD7,将134192写成0x20C30。

3.3 用基本数据类型表示字符(Character encoding form)

设计字符编码的第三层是用编程语言中的基本数据类型来表示字符。可以将这个层次理解成程序员看到的字符编码。在Unicode中,我们有很多方式将数字23383表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32。UTF是“UCS Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。例如,“汉字”对应的数字是0x6c49和0x5b57,而编码的程序数据是:

BYTE data_utf8[]={0xE6,0xB1,0x89,0xE5,0xAD,0x97}; // UTF-8编码WORD data_utf16[]={0x6c49,0x5b57}; // UTF-16编码DWORD data_utf32[]={0x6c49,0x5b57}; // UTF-32编码

这里用BYTE、WORD、DWORD分别表示无符号8位整数,无符号16位整数和无符号32位整数。UTF-8、UTF-16、UTF-32分别以BYTE、WORD、DWORD作为编码单位。

“汉字”的UTF-8编码需要6个字节。“汉字”的UTF-16编码需要两个WORD,大小是4个字节。“汉字”的UTF-32编码需要两个DWORD,大小是8个字节。4.2节会介绍将数字映射到UTF编码的规则。

3.4 作为字节流的字符(Character encoding scheme)

字符编码的第四层是计算机看到的字符,即在文件或内存中的字节流。例如,“字”的UTF-32编码是0x5b57,如果用little endian表示,字节流是“57 5b 00 00”。如果用big endian表示,字节流是“00 00 5b 57”。

字符编码的第三层规定了一个字符由哪些编码单位按什么顺序表示。字符编码的第四层在第三层的基础上又考虑了编码单位内部的字节序。UTF-8的编码单位是字节,不受字节序的影响。UTF-16、UTF-32根据字节序的不同,又衍生出UTF-16LE、UTF-16BE、UTF-32LE、UTF-32BE四种编码方案。LE和BE分别是Little Endian和Big Endian的缩写。

3.5 小结

通过四层模型,我们又把字符编码中发生的这些事情梳理了一遍。其实大多数代码页都不需要完整的四层模型,例如GB18030以字节为编码单位,直接规定了字节序列和字符的映射关系,跳过了第二层,也不需要第四层。

4 再谈Unicode

Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。码位就是可以分配给字符的数字。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。

Unicode字符集可以简写为UCS(Unicode Character Set)。早期的Unicode标准有UCS-2、UCS-4的说法。UCS-2用两个字节编码,UCS-4用4个字节编码。UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256行 (row),每行有256个码位(cell)。group 0的平面0被称作BMP(Basic Multilingual Plane)。将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。

Unicode标准计划使用group 0 的17个平面: 从BMP(平面0)到平面16,即数字0-0x10FFFF。《谈谈Unicode编码》主要介绍了BMP的编码,本文将介绍完整的Unicode编码,并从多个角度浏览Unicode。本文的介绍基于Unicode 5.0.0版本。

4.1 浏览Unicode

先看一些数字:每个平面有2^16=65536个码位。Unicode计划使用了17个平面,一共有17*65536=1114112个码位。其实,现在已定义的码位只有238605个,分布在平面0、平面1、平面2、平面14、平面15、平面16。其中平面15和平面16上只是定义了两个各占65534个码位的专用区(Private Use Area),分别是0xF0000-0xFFFFD和0x100000-0x10FFFD。所谓专用区,就是保留给大家放自定义字符的区域,可以简写为PUA。

平面0也有一个专用区:0xE000-0xF8FF,有6400个码位。平面0的0xD800-0xDFFF,共2048个码位,是一个被称作代理区(Surrogate)的特殊区域。它的用途将在4.2节介绍。

238605-65534*2-6400-2408=99089。余下的99089个已定义码位分布在平面0、平面1、平面2和平面14上,它们对应着Unicode目前定义的99089个字符,其中包括71226个汉字。平面0、平面1、平面2和平面14上分别定义了52080、3419、43253和337个字符。平面2的43253个字符都是汉字。平面0上定义了27973个汉字。

在更深入地了解Unicode字符前,我们先了解一下UCD。

4.1.1 什么是UCD

UCD是Unicode字符数据库(Unicode Character Database)的缩写。UCD由一些描述Unicode字符属性和内部关系的纯文本或html文件组成。大家可以在Unicode组织的网站看到UCD的最新版本

UCD中的文本文件大都是适合于程序分析的Unicode相关数据。其中的html文件解释了数据库的组织,数据的格式和含义。UCD中最庞大的文件无疑就是描述汉字属性的文件Unihan.txt。在UCD 5.0,0中,Unihan.txt文件大小有28,221K字节。Unihan.txt中包含了很多有参考价值的索引,例如汉字部首、笔划、拼音、使用频度、四角号码排序等。这些索引都是基于一些比较权威的辞典,但大多数索引只能检索部分汉字。

我介绍UCD的目的主要是为了使用其中的两个概念:Block和Script。

4.1.2 Block

UCD中的Blocks.txt将Unicode的码位分割成一些连续的Block,并描述了每个Block的用途:

开始码位结束码位Block名称(英文)Block名称(中文)0000007FBasic Latin基本拉丁字母008000FFLatin-1 Supplement拉丁字母补充-10100017FLatin Extended-A拉丁字母扩充-A0180024FLatin Extended-B拉丁字母扩充-B025002AFIPA Extensions国际音标扩充02B002FFSpacing Modifier Letters进格修饰字符0300036FCombining Diacritical Marks组合附加符号037003FFGreek and Coptic希腊文和哥普特文040004FFCyrillic西里尔文0500052FCyrillic Supplement西里尔文补充0530058FArmenian亚美尼亚文059005FFHebrew希伯来文060006FFArabic基本阿拉伯文0700074FSyriac叙利亚文0750077FArabic Supplement阿拉伯文补充078007BFThaana塔纳文07C007FFNKoN'Ko字母表0900097FDevanagari天成文书(梵文)098009FFBengali孟加拉文0A000A7FGurmukhi锡克教文0A800AFFGujarati古吉拉特文0B000B7FOriya奥里亚文0B800BFFTamil泰米尔文0C000C7FTelugu泰卢固文0C800CFFKannada卡纳达文0D000D7FMalayalam德拉维族文0D800DFFSinhala僧伽罗文0E000E7FThai泰文0E800EFFLao老挝文0F000FFFTibetan藏文1000109FMyanmar缅甸文10A010FFGeorgian格鲁吉亚文110011FFHangul Jamo朝鲜文1200137FEthiopic埃塞俄比亚文1380139FEthiopic Supplement埃塞俄比亚文补充13A013FFCherokee切罗基文1400167FUnified Canadian Aboriginal Syllabics加拿大印第安方言1680169FOgham欧甘文16A016FFRunic北欧古字1700171FTagalog塔加路文1720173FHanunoo哈努诺文1740175FBuhid布迪文1760177FTagbanwaTagbanwa文178017FFKhmer高棉文180018AFMongolian蒙古文1900194FLimbu林布文1950197FTai Le德宏傣文198019DFNew Tai Lue新傣文19E019FFKhmer Symbols高棉文1A001A1FBuginese布吉文1B001B7FBalinese巴厘文1D001D7FPhonetic Extensions拉丁字母音标扩充1D801DBFPhonetic Extensions Supplement拉丁字母音标扩充增补1DC01DFFCombining Diacritical Marks Supplement组合附加符号补充1E001EFFLatin Extended Additional拉丁字母扩充附加1F001FFFGreek Extended希腊文扩充2000206FGeneral Punctuation一般标点符号2070209FSuperscripts and Subscripts上标和下标20A020CFCurrency Symbols货币符号20D020FFCombining Diacritical Marks for Symbols符号用组合附加符号2100214FLetterlike Symbols似字母符号2150218FNumber Forms数字形式219021FFArrows箭头符号220022FFMathematical Operators数学运算符号230023FFMiscellaneous Technical零杂技术用符号2400243FControl Pictures控制图符2440245FOptical Character Recognition光学字符识别246024FFEnclosed Alphanumerics带括号的字母数字2500257FBox Drawing制表符2580259FBlock Elements方块元素25A025FFGeometric Shapes几何形状260026FFMiscellaneous Symbols零杂符号270027BFDingbats杂锦字型27C027EFMiscellaneous Mathematical Symbols-A零杂数学符号-A27F027FFSupplemental Arrows-A箭头符号补充-A280028FFBraille Patterns盲文2900297FSupplemental Arrows-B箭头符号补充-B298029FFMiscellaneous Mathematical Symbols-B零杂数学符号-B2A002AFFSupplemental Mathematical Operators数学运算符号2B002BFFMiscellaneous Symbols and Arrows零杂符号和箭头2C002C5FGlagolitic格拉哥里字母表2C602C7FLatin Extended-C拉丁字母扩充-C2C802CFFCoptic科普特文2D002D2FGeorgian Supplement格鲁吉亚文补充2D302D7FTifinagh提非纳字母2D802DDFEthiopic Extended埃塞俄比亚文扩充2E002E7FSupplemental Punctuation标点符号补充2E802EFFCJK Radicals Supplement中日韩部首补充2F002FDFKangxi Radicals康熙字典部首2FF02FFFIdeographic Description Characters汉字结构描述字符3000303FCJK Symbols and Punctuation中日韩符号和标点 3040309FHiragana平假名30A030FFKatakana片假名3100312FBopomofo注音符号3130318FHangul Compatibility Jamo朝鲜文兼容字母3190319FKanbun日文的汉字批注31A031BFBopomofo Extended注音符号扩充31C031EFCJK Strokes中日韩笔划31F031FFKatakana Phonetic Extensions片假名音标扩充320032FFEnclosed CJK Letters and Months带括号的中日韩字母及月份330033FFCJK Compatibility中日韩兼容字符34004DBFCJK Unified Ideographs Extension A中日韩统一表意文字扩充A4DC04DFFYijing Hexagram Symbols易经六十四卦象4E009FFFCJK Unified Ideographs中日韩统一表意文字A000A48FYi Syllables彝文音节A490A4CFYi Radicals彝文字根A700A71FModifier Tone Letters声调修饰字母A720A7FFLatin Extended-D拉丁字母扩充-DA800A82FSyloti NagriSyloti Nagri字母表A840A87FPhags-paPhags-pa字母表AC00D7AFHangul Syllables朝鲜文音节D800DB7FHigh Surrogates高位替代DB80DBFFHigh Private Use Surrogates高位专用替代DC00DFFFLow Surrogates低位替代E000F8FFPrivate Use Area专用区F900FAFFCJK Compatibility Ideographs中日韩兼容表意文字FB00FB4FAlphabetic Presentation Forms字母变体显现形式FB50FDFFArabic Presentation Forms-A阿拉伯文变体显现形式-A FE00FE0FVariation Selectors字型变换选取器FE10FE1FVertical Forms竖排标点符号FE20FE2FCombining Half Marks组合半角标示FE30FE4FCJK Compatibility Forms中日韩兼容形式FE50FE6FSmall Form Variants小型变体形式FE70FEFFArabic Presentation Forms-B阿拉伯文变体显现形式-B FF00FFEFHalfwidth and Fullwidth Forms半角及全角字符FFF0FFFFSpecials特殊区域100001007FLinear B Syllabary线形文字B音节文字10080100FFLinear B Ideograms线形文字B表意文字101001013FAegean Numbers爱琴海数字101401018FAncient Greek Numbers古希腊数字103001032FOld Italic古意大利文103301034FGothic哥特文103801039FUgaritic乌加里特楔形文字103A0103DFOld Persian古波斯文104001044FDeseret德塞雷特大学音标104501047FShavian肃伯纳速记符号10480104AFOsmanyaOsmanya字母表108001083FCypriot Syllabary塞浦路斯音节文字109001091FPhoenician腓尼基文10A0010A5FKharoshthi迦娄士悌文12000123FFCuneiform楔形文字124001247FCuneiform Numbers and Punctuation楔形文字数字和标点1D0001D0FFByzantine Musical Symbols东正教音乐符号1D1001D1FFMusical Symbols音乐符号1D2001D24FAncient Greek Musical Notation古希腊音乐符号1D3001D35FTai Xuan Jing Symbols太玄经符号1D3601D37FCounting Rod Numerals算筹1D4001D7FFMathematical Alphanumeric Symbols数学用字母数字符号200002A6DFCJK Unified Ideographs Extension B中日韩统一表意文字扩充 B2F8002FA1FCJK Compatibility Ideographs Supplement中日韩兼容表意文字补充E0000E007FTags标签E0100E01EFVariation Selectors Supplement字型变换选取器补充F0000FFFFFSupplementary Private Use Area-A补充专用区-A10000010FFFFSupplementary Private Use Area-B补充专用区-B

Block是Unicode字符的一个属性。属于同一个Block的字符有着相近的用途。Block表中的开始码位、结束码位只是用来划分出一块区域,在开始码位和结束码位之间可能还有很多未定义的码位。使用UniToy,大家可以按照Block浏览Unicode字符,既可以按列表显示:

也可以显示每个字符的详细信息:

4.1.3 Script

Unicode中每个字符都有一个Script属性,这个属性表明字符所属的文字系统。Unicode目前支持以下Script:

Script名称(英文)Script名称(中文)Script包含的字符数Arabic阿拉伯文966Armenian亚美尼亚文90Balinese巴厘文121Bengali孟加拉文91Bopomofo汉语注音符号64Braille盲文256Buginese布吉文30Buhid布迪文20Canadian Aboriginal加拿大印第安方言630Cherokee切罗基文85CommonCommon5020Coptic科普特文128Cuneiform楔形文字982Cypriot塞浦路斯音节文字55Cyrillic西里尔文277Deseret德塞雷特大学音标80Devanagari天成文书(梵文)107Ethiopic埃塞俄比亚文461Georgian格鲁吉亚文120Gothic哥特文94Glagolitic格拉哥里字母表27Greek希腊文506Gujarati古吉拉特文83Gurmukhi锡克教文77Han汉文71570Hangul韩文书写系统11619Hanunoo哈努诺文21Hebrew希伯来文133Hiragana平假名89InheritedInherited461Kannada卡纳达文86Katakana片假名164Kharoshthi迦娄士悌文65Khmer高棉文146Lao老挝文65Latin拉丁文系1070Limbu林布文(尼泊尔东部)66Linear B线形文字B211Malayalam德拉维族文(印度)78Mongolian蒙古文152Myanmar缅甸文78New Tai Lue新傣文80NkoN'Ko字母表59Ogham欧甘文字29Old Italic古意大利文35Old Persian古波斯文50Oriya奥里亚文81OsmanyaOsmanya字母表40Phags PaPhags Pa字母表(蒙古)56Phoenician腓尼基文27Runic古代北欧文78Shavian肃伯纳速记符号48Sinhala僧伽罗文80Syloti NagriSyloti Nagri字母表(印度)44Syriac叙利亚文77Tagalog塔加路文(菲律宾)20TagbanwaTagbanwa文(菲律宾)18Tai Le德宏傣文35Tamil泰米尔文71Telugu泰卢固文(印度)80Thaana马尔代夫书写体 50Thai泰国文86Tibetan藏文195Tifinagh提非纳字母表55Ugaritic乌加里特楔形文字31Yi彝文1220

其中,有两个Script值有着特殊的含义:

Common:Script属性为Common的字符可能在多个文字系统中使用,不是某个文字系统特有的。例如:空格、数字等。 Inherited:Script属性为Inherited的字符会继承前一个字符的Script属性。主要是一些组合用符号,例如:在“组合附加符号”区(0x300-0x36f),字符的Script属性都是Inherited。

UCD中的Script.txt列出了每个字符的Script属性。使用UniToy可以按照Script属性查看字符。例如:

左侧Script窗口中,第一层节点是按英文字母顺序排列的Script属性。第二层节点是包含该Script文字的行(row),点击后显示该行内属于这个Script的字符。这样,就可以集中查看属于同一文字系统的字符。

4.1.4 Unicode中的汉字

前面提过,在Unicode已定义的99089个字符中,有71226个字符是汉字。它们的分布如下:

Block名称开始码位结束码位数量中日韩统一表意文字扩充A34004db56582中日韩统一表意文字4e009fbb20924中日韩兼容表意文字f900fa2d302中日韩兼容表意文字fa30fa6a59中日韩兼容表意文字fa70fad9106中日韩统一表意文字扩充B200002a6d642711中日韩兼容表意文字补充2f8002fa1d542

UCD的Unihan.txt中的部首偏旁索引(kRSUnicode)可以检索全部71226个汉字。kRSUnicode的部首是按照康熙字典定义的,共214个部首。简体字按照简体部首对应的繁体部首检索。UniToy整理了康熙字典部首对应的简体部首,提供了按照部首检索汉字的功能:

4.2 UTF编码

在字符编码的四个层次中,第一层的范围和第二层的编码在4.1节已经详细讨论过了。本节讨论第三层的UTF编码和第四层的字节序,主要谈谈第三层的UTF编码,即怎样将Unicode定义的编码转换成程序数据。

4.2.1 UTF-8

UTF-8以字节为单位对Unicode进行编码。从Unicode到UTF-8的编码方式如下:

Unicode编码(16进制)UTF-8 字节流(二进制)000000 - 00007F0xxxxxxx000080 - 0007FF110xxxxx 10xxxxxx000800 - 00FFFF1110xxxx 10xxxxxx 10xxxxxx010000 - 10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。

例1:“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。

例2:“”字的Unicode编码是0x20C30。0x20C30在0x010000-0x10FFFF之间,使用用4字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将0x20C30写成21位二进制数字(不足21位就在前面补0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0 B0 B0。

4.2.2 UTF-16

UniToy有个“输出编码”功能,可以输出当前选择的文本编码。因为UniToy内部采用UTF-16编码,所以输出的编码就是文本的UTF-16编码。例如:如果我们输出“汉”字的UTF-16编码,可以看到0x6C49,这与“汉”字的Unicode编码是一致的。如果我们输出“”字的UTF-16编码,可以看到0xD843, 0xDC30。“”字的Unicode编码是0x20C30,它的UTF-16编码是怎样得到的呢?

4.2.2.1 编码规则

UTF-16编码以16位无符号整数为单位。我们把Unicode编码记作U。编码规则如下:

如果U<0x10000,U的UTF-16编码就是U对应的16位无符号整数(为书写简便,下文将16位无符号整数记作WORD)。 如果U≥0x10000,我们先计算U'=U-0x10000,然后将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

为什么U'可以被写成20个二进制位?Unicode的最大码位是0x10ffff,减去0x10000后,U'的最大值是0xfffff,所以肯定可以用20个二进制位表示。例如:“”字的Unicode编码是0x20C30,减去0x10000后,得到0x10C30,写成二进制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD843 0xDC30。

4.2.2.2 代理区(Surrogate)

按照上述规则,Unicode编码0x10000-0x10FFFF的UTF-16编码有两个WORD,第一个WORD的高6位是110110,第二个WORD的高6位是110111。可见,第一个WORD的取值范围(二进制)是11011000 00000000到11011011 11111111,即0xD800-0xDBFF。第二个WORD的取值范围(二进制)是11011100 00000000到11011111 11111111,即0xDC00-0xDFFF。

为了将一个WORD的UTF-16编码与两个WORD的UTF-16编码区分开来,Unicode编码的设计者将0xD800-0xDFFF保留下来,并称为代理区(Surrogate):

D800DB7FHigh Surrogates高位替代DB80DBFFHigh Private Use Surrogates 相关资源:自动抢茅台脚本.zip
转载请注明原文地址: https://www.6miu.com/read-3850312.html

最新回复(0)