UCS2 UCS4 中文字符编码 TTF字库之间的关系Word文档格式.docx
《UCS2 UCS4 中文字符编码 TTF字库之间的关系Word文档格式.docx》由会员分享,可在线阅读,更多相关《UCS2 UCS4 中文字符编码 TTF字库之间的关系Word文档格式.docx(18页珍藏版)》请在冰点文库上搜索。
(space)
64
@
96
、
1
SOH
33
!
65
A
97
a
2
STX
34
”
66
B
98
b
3
ETX
35
#
67
C
99
c
4
EOT
36
$
68
D
100
d
5
ENQ
37
%
69
E
101
e
6
ACK
38
&
70
F
102
f
7
BEL
39
71
G
103
g
8
BS
40
(
72
H
104
h
9
HT
41
)
73
I
105
i
10
LF
42
*
74
J
106
j
11
VT
43
+
75
K
107
k
12
FF
44
76
L
108
l
13
CR
45
-
77
M
109
m
14
SO
46
.
78
N
110
n
15
SI
47
/
79
O
111
o
16
DLE
48
80
P
112
p
17
DCI
49
81
Q
113
q
18
DC2
50
82
R
114
r
19
DC3
51
83
X
115
s
20
DC4
52
84
T
116
t
21
NAK
53
85
U
117
u
22
SYN
54
86
V
118
v
23
TB
55
87
W
119
w
24
CAN
56
88
120
x
25
EM
57
89
Y
121
y
26
SUB
58
90
Z
122
z
27
ESC
59
;
91
[
123
{
28
FS
60
<
92
124
|
29
GS
61
=
93
]
125
}
30
RS
62
>
94
^
126
~
31
US
63
95
—
127
DEL
ASCII收录了空格及94个“可印刷字符”,足以给英语使用。
但是,其他使用拉丁字母的语言(主要是欧洲国家的语言),都有一定数量的变音字母,故可以使用ASCII及控制字符以外的区域来储存及表示。
除了使用拉丁字母的语言外,使用西里尔字母的东欧语言、希腊语、泰语、现代阿拉伯语、希伯来语等,都可以使用这个形式来储存及表示。
很明显这种编码表示的字符范围很窄,无法表示中文字符。
汉字编码
区位码国标码
1980年,为了使每一个汉字有一个全国统一的代码,我国颁布了第一个汉字编码的国家标准:
GB2312-80《信息交换用汉字编码字符集》基本集,这个字符集是我国中文信息处理技术的发展基础,也是目前国内所有汉字系统的统一标准。
这个标准用两个数来编码汉字和中文符号。
第一个数称为“区”,第二个数称为“位”。
所以也称为区位码。
1-9区是中文符号,16-55区是一级汉字,56-87区是二级汉字。
国标码是一个四位十六进制数,区位码是一个四位的十进制数,每个国标码或区位码都对应着一个唯一的汉字或符号,但因为十六进制数我们很少用到,所以大家常用的是区位码,它的前两位叫做区码,后两位叫做位码。
用一句简单的话说就是区位码人使用起来比较方便,而国标码是为了给计算机使用。
当然一会讲到两者的关系。
总之这个GB2312-80就是一张大表,这张94*94的大表规定了哪个汉字用哪个编码(数字)来代替。
例如”土”的区位码4533。
它表示“土“字在45区的33位.其在表中的位置可以如下图所示:
那它又是如何转为国标码的呢。
按规定把区位码的区和位分别+160就是相应的国标码,45+160=205=0xCD,33+160=193=0xC1那么0xCDC1就是”土“的国标码。
为什么要加上0xA0呢,主要是为了和ASCII的编码区分开来,以便让计算机知道是一个字节的英文还是两个字节的中文,现在计算机知道了所有的中文第一个字节都大于0xA0,由于经常说起人们还给他起了一个名字(LeadingByte)。
这种表示的字符集又叫MBCS因为WINDOWS只使用了两个字节所以又叫DBCS。
大端和小端模式
现在我们知道了在计算机里是用两个字节来表示一个中文字符的。
现在有一个问题。
当我们打开一个文本文件,并读取到了“D7D6B7FBBACDB1E0C2EB”这十个字节。
现在我们知道D7D6根据国标码规则应该是一个汉字,但这究竟应该是0xD7D6呢,还是0xD6D7呢。
这就是有名的大端模式和小端模式。
bigendian和littleendian(大端模式和小端模式)“endian”这个词出自《格列佛游记》。
小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将bigendian和littleendian称作“大尾”和“小尾”。
所谓大端模式就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
小端模式就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
下面是把一个LONG型的数据0x存放到内存里里两种方式的排列方法:
在我的X86结构的计算机里,用的是小端模式。
那么其中”D7D6”就是国标码的”字”这个字符。
代码页
现在我们知道了在中国大陆我们使用我们的GBK,那在台湾,在日本,在韩国使用什么呢没错如你所猜测的,每个地区都有每个地区的标准。
我们的叫GBK而台湾的就叫Big5。
由于自己制定的标准那么就出现了一个编码按不同的编码出现两个符号的情况。
0xD1C6这个国标码在GB2312的编码下是一个”哑”字,而在Big5的编码下却是一个“
“字。
那么WINDOWS到底应该把这个编码译为哪个字符呢。
又是如何译的呢这就不得不说代码页。
代码页(CodePage)是个古老的专业术语,据说是IBM公司首先使用的。
代码页和字符集的含义基本相同,代码页规定了适用于特定地区的字符集合,和这些字符的编码。
可以将代码页理解为字符和字节数据的映射表。
Windows为自己支持的代码页都编了一个号码。
例如代码页936就是简体中文GBK,代码页950就是繁体中文Big5。
代码页的概念比较简单,就是一个字符编码方案。
这个方案由当地的编码机构进行制定。
但要说清楚Windows的ANSI代码页,就要从Windows的区域(Locale)说起了。
微软为了适应世界上不同地区用户的文化背景和生活习惯,在Windows中设计了区域(Locale)设置的功能。
Local是指特定于某个国家或地区的一组设定,包括代码页,数字、货币、时间和日期的格式等。
在Windows内部,其实有两个Locale设置:
系统Locale和用户Locale。
系统Locale决定代码页,用户Locale决定数字、货币、时间和日期的格式。
我们可以在控制面板的“区域和语言选项”中设置系统Locale和用户Locale:
每个Locale都有一个对应的代码页。
系统Locale对应的代码页被作为Windows的默认代码页。
在没有文本编码信息时,Windows按照默认代码页的编码方案解释文本数据。
这个默认代码页通常被称作ANSI代码页(ACP)。
ANSI代码页还有一层意思,就是微软自己定义的代码页。
在历史上,IBM的个人计算机和微软公司的操作系统曾经是PC的标准配置。
微软公司将IBM公司定义的代码页称作OEM代码页,在IBM公司的代码页基础上作了些增补后,作为自己的代码页,并冠以ANSI的字样。
我们在“区域和语言选项”高级页面的代码页转换表中看到的包含ANSI字样的代码页都是微软自己定义的代码页。
例如:
874(ANSI/OEM-泰文)
932(ANSI/OEM-日文Shift-JIS)
936(ANSI/OEM-简体中文GBK)
949(ANSI/OEM-韩文)
950(ANSI/OEM-繁体中文Big5)
1250(ANSI-中欧)
1251(ANSI-西里尔文)
1252(ANSI-拉丁文I)
1253(ANSI-希腊文)
1254(ANSI-土耳其文)
1255(ANSI-希伯来文)
1256(ANSI-阿拉伯文)
1257(ANSI-波罗的海文)
1258(ANSI/OEM-越南)
我们不能直接设置ANSI代码页,只能通过选择系统Locale,间接改变当前的ANSI代码页。
微软定义的Locale只使用自己定义的代码页。
所以,我们虽然可以通过“区域和语言选项”中的代码页转换表安装很多代码页,但只能将微软的代码页作为系统默认代码页。
在Windows2000以后,Windows统一采用UTF-16作为内部字符编码。
现在,安装一个代码页就是安装一张代码页转换表。
通过代码页转换表,Windows既可以将代码页的编码转换到UTF-16,也可以将UTF-16转换到代码页的编码。
代码页转换表的具体实现可以是一个以nls为后缀的数据文件,也可以是一个提供转换函数的动态链接库。
有的代码页是不需要安装的。
Windows将UTF-7和UTF-8分别作为代码页65000和代码页65001。
UTF-7、UTF-8和UTF-16都是基于Unicode的编码方案。
它们之间可以通过简单的算法直接转换,不需要安装代码页转换表。
在安装过一个代码页后,Windows就知道怎样将该代码页的文本转换到Unicode文本,也知道怎样将Unicode文本转换成该代码页的文本。
如何把文本文件打开的
当我们打开了文本文件,并知道它有十个字节的数据“D7D6B7FBBACDB1E0C2EB”。
Windows首先根据locale来查看当前的代码页是多少,当然我的计算机是936于是它按GB2312来查找相应的字符。
它得到DBCS字符串”字节和字符”。
没错,代码页就是这么有用而且巧妙,微软不管你什么规则,不管你什么字符,总之我按你给我的标准来。
至于你是什么微软不关心。
由于各代码页支持的字符范围不同,我们一般不会直接在代码页间转换文本。
需要我们转换文本时,我们先把A(要转换的文本)转换为unicode码,然后再把这个unicode码转换为B(转换后的文本)。
如何把简体中文转换为Big5编码的繁体中文
将GBK中文简体编码的文本转换到Big5繁体编码的正确步骤应该是:
1.将GBK中文简体编码映射到GBK的中文繁体的;
2.GBK的中文繁体转换为Unicode中文繁体文本;
3.Unicode中文繁体的文本转换为Big5繁体的文本。
1.将GBK中文简体编码映射到GBK的中文繁体的
LCIDlcid=MAKELCID(MAKELANGID(LANG_CHINESE,
SUBLANG_CHINESE_SIMPLIFIED),SORT_CHINESE_PRC);
intnLength=LCMapStringA(lcid,LCMAP_TRADITIONAL_CHINESE,
szGBKString,-1,NULL,0);
GBK的中文繁体转换为Unicode中文繁体文本;
UINTnCodePage=936;
Unicode中文繁体的文本转换为Big5繁体的文本。
UINTnCodePage=950;
//BIG5
intnLength=WideCharToMultiByte(nCodePage,0,szUnicodeString,
-1,NULL,0,NULL,NULL);
char*pBuffer=newchar[nLength+1];
WideCharToMultiByte(nCodePage,0,szUnicodeString,-1,pBuffer,nLength,NULL,NULL);
pBuffer[nLength]=0;
为什么要这样转换呢如果你有一张区位码表你就会发现,一个简体的“门”(3537)和一个繁体的“門”(7376)在区位码里是两个编码。
而BIG5里没有”门”相对应的字的,所以如果直接把GBK的”门”转换为Unicode的“门“,然后unicode的”门”到转换为Big5“門”的时候,就找不到这个字就会出现””,请记住这个本文开头就出现了一个””。
为了避免这种情况,我们把GBK的所有简体字,用LCMapStringA映射为繁体字。
但GBK的繁体字的编码也不是Big5的编码,两者没有任何联系,但两者在unicode里是同一个编码。
没错UNICODE所有的编码都是唯一的。
UCS-4
Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。
Unicode的学名是"
UniversalMultiple-OctetCodedCharacterSet"
,简称为UCS。
UCS可以看作是"
UnicodeCharacterSet"
的缩写。
历史上存在两个试图独立设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会()。
ISO开发了ISO10646项目,Unicode协会开发了Unicode项目。
在1991年前后,双方都认识到世界不需要两个不兼容的字符集。
于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。
从开始,Unicode项目采用了与ISO10646-1相同的字库和字码。
目前两个项目仍都存在,并独立地公布各自的标准。
Unicode协会现在的最新版本是2005年的Unicode。
ISO的最新标准是ISO10646-3:
2003。
UCS只是规定如何编码,并没有规定如何传输、保存这个编码。
例如“汉”字的UCS编码是6C49,我可以用4个ascii数字来传输、保存这个编码;
也可以用utf-8编码:
3个连续的字节E6B189来表示它。
关键在于通信双方都要认可。
UTF-8、UTF-7、UTF-16都是被广泛接受的方案。
UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。
UTF是“UCSTransformationFormat”的缩写。
IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。
我总是记不得IETF是InternetEngineeringTaskForce的缩写。
但IETF负责维护的RFC是Internet上一切规范的基础。
目前Windows的内核已经支持Unicode字符集,这样在内核上可以支持全世界所有的语言文字。
但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。
Windows使用代码页(codepage)来适应各个国家和地区。
codepage可以被理解为前面提到的内码。
GBK对应的codepage是CP936。
微软也为GB18030定义了codepage:
CP54936。
但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个codepage是无法真正使用的。
UCS有两种格式:
UCS-2和UCS-4。
顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。
下面让我们做一些简单的数学游戏:
UCS-2有2^16=65536个码位,UCS-4有2^31=48个码位。
UCS-4根据最高位为0的最高字节分成2^7=128个group。
每个group再根据次高字节分为256个plane。
每个plane根据第3个字节分为256行(rows),每行包含256个cells。
当然同一行的cells只是最后一个字节不同,其余都相同。
group0的plane0被称作BasicMultilingualPlane,即BMP。
或者说UCS-4中,高两个字节为0的码位被称作BMP。
将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。
在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。
而目前的UCS-4规范中还没有任何字符被分配在BMP之外。
UCS-4分配示意图
和UTF16
UTF-8就是以8位为单元对UCS进行编码。
从UCS-4到UTF-8的编码方式如下:
Ucs-2到UTF8编码如下
UCS-2编码(16进制)UTF-8字节流(二进制)
0000-007F0xxxxxxx
0080-07FF110xxxxx10xxxxxx
0800-FFFF1110xxxx10xxxxxx10xxxxxx
例如“汉”字的Unicode编码是6C49。
6C49在0800-FFFF之间,所以肯定要用3字节模板了:
1110xxxx10xxxxxx10xxxxxx。
将6C49写成二进制是:
0110110001001001,用这个比特流依次代替模板中的x,得到:
001,即E6B189。
UTF-16以16位为单元对UCS进行编码。
对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。
对于不小于0x10000的UCS码,定义了一个算法。
不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。
但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。
UTF-8以字节为编码单元,没有字节序的问题。
UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。
例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。
如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”Unicode规范中推荐的标记字节顺序的方法是BOM。
BOM不是“BillOfMaterial”的BOM表,而是ByteOrderMark。
BOM是一个有点小聪明的想法:
在UCS编码中有一个叫做"
ZEROWIDTHNO-BREAKSPACE"
的字符,它的编码是FEFF。
而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。
UCS规范建议我们在传输字节流前,先传输字符"
ZEROWIDTHNO-BREAKSPACE"
。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;
如果收到FFFE,就表明这个字节流是Little-Endian的。
因此字符"
又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。
字符"
的UTF-8编码是EFBBBF(读者可以用我们前面介绍的编码方法验证一下)。
所以如果接收者收到以EFBBBF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。
总结字符的编码
通过前几页的学习我们知道,汉字的编码方法和任何一个字符编码的方法。
也知道了WINDOWS如何解析这个编码。
现在再回来说这个“↵”,由于这个符号是UNICODE码为0x21B5的一个字符,那么当我向U