业精于勤,荒于嬉
全部博文(763)
分类:
2009-10-16 19:50:45
1.字符集和字符编码介绍
字符集简而言之就是文字的集合,英语环境国家的字符集包括大小写字母,数字,标点符号以及一 些常用的控制用非显示字符以及特殊符号等,而非英语环境国家,比如中国,除了要使用这些,当然要再字符集中包含中国的文字,以及中国的标点符号,这些构成 中国的字符集。由于世界各国的文化差异和各国家内的文化差异,字符集的种类非常多。比如,英语国家的Ascii(由ANSI标准化)字符集,中国的简体中 文常用字符集,台湾的繁体中文字符集,日本和韩国的字符集等等。
上面说的字符集是某一个语言环境需要的字符集合,即我们在计算机上看到的文字或符号等。字符 编码就是这些符号在计算机中的数字化表示规则。这些规则需要大家都遵守,这就是字符编码标准。在中国简体中文常用字符集的字符编码是国家标准 GB2312-80,繁体加简体字符编码标准是GBK等等。台湾是繁体中文的语言环境,说明其字符集是繁体中文,但其字符编码标准不遵循GBK,而是其自 行设计的规则(GIG5字符编码,而香港的繁体中文字符集又采用不同的字符编码HKSCS。各种不同的字符编码在ANSI注册为不同的代码页,而且大多数 的代码页不相兼容,因此就出现的unicode字符编码,意在将全世界的各种语言环境合成一个大字符集,用统一的字符编码来表示。
2.计算机字符编码发展过程
字符与编码的发展从计算机对多国语言的支持角度看,大致可以分为三个阶段:
|
系统内码 |
说明 |
系统 |
阶段一 |
ASCII |
计算机刚开始只支持英语,其它语言不能够在计算机上存储和显示。 |
英文 DOS |
阶段二 |
ANSI编码 (本地化) |
为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如:汉字 \'中\' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。 不同的国家 和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。 不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。 |
中文 DOS,中文 Windows 95/98,日文 Windows 95/98 |
阶段三 |
UNICODE (国际化) |
为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。 |
Windows NT/2000/XP,Linux,Java |
在 ASCII 阶段,单字节字符串使用一个字节存放一个字符(SBCS)。比如,Bob123 在内存中为:
42 |
6F |
62 |
31 |
32 |
33 |
00 |
|
|
|
|
|
|
|
B |
o |
b |
1 |
2 |
3 |
\\0 |
在使用 ANSI 编码支持多种语言阶段,每个字符使用一个字节或多个字节来表示(MBCS),因此,这种方式存放的字符也被称作多字节字符。比如,中文123 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节:
D6 |
D0 |
CE |
C4 |
31 |
32 |
33 |
00 |
|
|
|
|
|
| ||
中 |
文 |
1 |
2 |
3 |
\\0 |
在 UNICODE 被采用之后,计算机存放字符串时,改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节(16 位)来存放一个序号(DBCS),因此,这种方式存放的字符也被称作宽字节字符。比如,字符串 中文123 在 Windows 2000 下,内存中实际存放的是 5 个序号:
2D |
4E |
87 |
65 |
31 |
00 |
32 |
00 |
33 |
00 |
00 |
00 |
← 在 x86 CPU 中,低字节在前 |
|
|
|
|
|
|
| ||||||
中 |
文 |
1 |
2 |
3 |
\\0 |
|
一共占 10 个字节。
上述文字中的相关术语解释:
|
概念描述 |
举例 |
字符 |
人们使用的记号,抽象意义上的一个符号。 |
\'1\', \'中\', \'a\', \'$\', \'¥\', …… |
字节 |
计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。 |
0x01, 0x45, 0xFA, …… |
ANSI 字符串 |
在内存中,如果“字符”是以 ANSI 编码形式存在的,一个字符可能使用一个字节或多个字节来表示,那么我们称这种字符串为 ANSI 字符串或者多字节字符串。 |
中文123 (占7字节) |
UNICODE 字符串 |
在内存中,如果“字符”是以在 UNICODE 中的序号存在的,那么我们称这种字符串为 UNICODE 字符串或者宽字节字符串。 |
L中文123 (占10字节) |
3.字符编码规则分类
分类 |
编码标准 |
说明 |
单字节字符编码 |
ISO-8859-1 |
最简单的编码规则,每一个字节直接作为一个 UNICODE 字符。比如,[0xD6, 0xD0] 这两个字节,通过 iso-8859-1 转化为字符串时,将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 ÖÐ。 反之,将 UNICODE 字符串通过 iso-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。 |
ANSI 编码 |
GB2312, BIG5, Shift_JIS, ISO-8859-2 …… |
把 UNICODE 字符串通过 ANSI 编码转化为“字节串”时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。 反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。比如,[0xD6, 0xD0] 这两个字节,通过 GB2312 转化为字符串时,将得到 [0x4E2D] 一个字符,即 \'中\' 字。 “ANSI 编码”的特点: 1. 这些“ANSI 编码标准”都只能处理各自语言范围之内的 UNICODE 字符。 2. “UNICODE 字符”与“转换出来的字节”之间的关系是人为规定的。 |
UNICODE 编码 |
UTF-8, UTF-16, UnicodeBig …… |
与“ANSI 编码”类似的,把字符串通过 UNICODE 编码转化成“字节串”时,一个 UNICODE 字符可能转化成一个字节或多个字节。 与“ANSI 编码”不同的是: 1. 这些“UNICODE 编码”能够处理所有的 UNICODE 字符。 2. “UNICODE 字符”与“转换出来的字节”之间是可以通过计算得到的。 |
4.常见字符编码介绍
ASCII 码:
ASCII是用来表示英文字符的一种编码规范,使用单字节内码(Single-Byte character sets (SBCS)),每个ASCII字符占用1个字节(8bits) 因此,ASCII编码可以表示的最大字符数是256,其实英文字符并没有那么多,一般只用前128个(最高位为0), 即低7 位(00~7F)。 32 ~ 127 表示字符。32 是空格, 32 以下是控制字符(不可见)。其中包括了控制字符、数字、大小写字母和其他一些符号 。最后就 128 位以下的用处达成共识,制定了 ASCII 标准。而最高位为1的另128个字符被成为“扩展ASCII”,在空置的0xA0-0xFF的范围内,加入192个字母及符号,一般用来存放英文的制表 符、部分音标字符等等的一些其他符号,藉以供使用的语言使用。即ISO/IEC 8859-1,又称Latin-1或“西欧语言”。
ISO-8859-1:
ISO/IEC 8859-1,又称Latin-1或“西欧语言”,是内的第一个8位字符集。它以为基础,在空置的0xA0-0xFF的范围内,加入192个字母及符号,藉以供使用的语言使用。
此字符集支援部分于使用的语言,包括、、、、、、、、、、、、、、、、、、、、及。
虽然没有重音字母,但仍会标明为ISO 8859-1编码。除此之外,欧洲以外的部分语言,如、、及、菲律宾等也可使用ISO 8859-1编码。
及芬兰语本来也使用ISO 8859-1来表示。但因它没有法语使用的 œ、Œ、 Ÿ 三个字母及芬兰语使用的 Š、š、Ž、ž ,故于1998年被所取代。(ISO 8859-15同时加入了符号)
ISO/IEC 8859-1 | ||||||||||||||||
|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
A |
B |
C |
D |
E |
F |
0- |
| |||||||||||||||
1- |
| |||||||||||||||
2- |
SP |
! |
" |
# |
$ |
% |
& |
' |
( |
) |
* |
+ |
, |
- |
. |
/ |
3- |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
: |
; |
< |
= |
> |
? |
4- |
@ |
A |
B |
C |
D |
E |
F |
G |
H |
I |
J |
K |
L |
M |
N |
O |
5- |
P |
Q |
R |
S |
T |
U |
V |
W |
X |
Y |
Z |
[ |
\ |
] |
^ |
_ |
6- |
` |
a |
b |
c |
d |
e |
f |
g |
h |
i |
j |
k |
l |
m |
n |
o |
7- |
p |
q |
r |
s |
t |
u |
v |
w |
x |
y |
z |
{ |
| |
} |
~ |
|
8- |
| |||||||||||||||
9- |
| |||||||||||||||
A- |
NBSP |
¡ |
¢ |
£ |
¤ |
¥ |
¦ |
§ |
¨ |
© |
ª |
« |
¬ |
SHY |
® |
¯ |
B- |
° |
± |
² |
³ |
´ |
µ |
¶ |
· |
¸ |
¹ |
º |
» |
¼ |
½ |
¾ |
¿ |
C- |
||||||||||||||||
D- |
× |
|||||||||||||||
E- |
||||||||||||||||
F- |
÷ |
在上表中,0x20是、0xA0是不换行空格、0xAD是选择性。
0x00-0x1F、0x7F、0x80-0x9F在此字符集中未有定义。(控制字符是由ISO 6429及ISO 4873定义)。
GB2312-80、GBK、GB18030、BIG5、HKSCS:
GB2312-80,ANSI代码页936,是一种双字节内码(Double-Byte character sets)(DBCS),。是中国大陆使用的国家标准,其中一共编码了6763个常用简体汉字。Big5,是台湾使用的编码标准,编码了台湾使用的繁体汉 字,大概有8千多个。HKSCS,是中国香港使用的编码标准,字体也是繁体,但跟Big5有所不同。 这3套编码标准都采用了两个扩展ASCII的方法,因此,几套编码互不兼容,而且编码区间也各有不同 因为其不兼容性,在同一个系统中同时显示GB和Big5基本上是不可能的。后来,由于各方面的原因,国际上又制定了针对中文的统一字符集GBK和 GB18030,GBK兼容GB2312,并增加了大量不常用汉字,还加入了几乎所有的Big5中的繁体汉字。但是GBK中的繁体汉字和Big5中的几乎 不兼容。 GB18030相当于是GBK的超集,比GBK包含的字符更多。
但是这些方法有问题,最大的问题就是,中文文字没有真正属于自己的编码,因为扩展ASCII 码虽然没有真正的标准化,但是PC里的ASCII码还是有一个事实标准的(存放着英文制表符),所以很多软件利用这些符号来画表格。这样的软件用到中文系 统中,这些表格符就会被误认作中文字,破坏版面。而且,统计中英文混合字符串中的字数,也是比较复杂的,我们必须判断一个ASCII码是否扩展,以及它的 下一个ASCII是否扩展,然后才“猜”那可能是一个中文字。
Unicode:
亚洲语言的字符集通常数以千计, 8 位已经不足以表达,这通常用一种很凌乱的,叫做 DBCS(双字节字符集,double byte character set) 的系统来解决。这种系统中,有些字符占用 1 字节,有些 2 字节。这样一来,在字符串中向前解析很容易,而倒退却很麻烦。这些不同的假设(code page)在单个的机器上没有问题。而随着 Internet 的发展,字符串要从一个机器上移到另一个机器上,这就产生了问题。于是, Unicode 出现了。Unicode 是一个勇敢的成就。它把在这个星球上的每一个合理的文字系统整合成了一个单一的字符集。
Unicode有两套标准,一套叫UCS-2(Unicode-16),用2个字节为字符编 码,另一套叫UCS-4(Unicode-32),用4个字节为字符编码。以目前常用的UCS-2为例,它可以表示的字符数为2^16=65535,基本 上可以容纳所有的欧美字符和绝大部分的亚洲字符。unicode字母表中的每一个抽象的字母,都被赋予了一个数字,比如 U+0645. 这个叫做 code point。用U+ 表示:Unicode, 数字是 16 进制的。你可以通过 charmap 命令来查看所有这些编码。(Windows 2000/XP 中). 或者访问 Unicode 的网站)
Unicode 中 code point 的数字的大小是没有限制的,而且也早就超过了 65535. 所以不是每个字符都能存储在两个字节中。那么,一个字符串 "Hello", 在 Unicode 中会表示成 5 个 code points :
U+0048 U+0065 U+006C U+006C U+006F
在Unicode里,所有的字符被一视同仁。汉字不再使用“两个扩展ASCII”,而是使用“1个Unicode”,注意,现在的汉字是“一个字符”了,于是,拆字、统计字数这些问题也就自然而然的解决了 。
但是Unicode也带来了一些新的问题:
1.和ASCII字符集之间的不兼容
2.C语言使用'\0'作为字符串结尾,而Unicode里恰恰有很多字符都有一个字节为0,这样一来,C语言的字符串函数将无法正常处理Unicode,除非把世界上所有用C写的程序以及他们所用的函数库全部换掉
为了解决这些问题,UTF诞生了
UTF:UCS Transformation Format
UCS转换格式,它是将Unicode编码规则和计算机的实际编码对应起来的一个规则。分为:UTF-7、UTF-8、UTF-16和UTF-32,现在常用的有UTF-8和UTF-16
UTF-8:以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:
UCS-2编码(16进制) |
UTF-8 字节流(二进制) |
0000 - 007F |
0xxxxxxx |
0080 - 07FF |
110xxxxx 10xxxxxx |
0800 - FFFF |
1110xxxx 10xxxxxx 10xxxxxx |
例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定 要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
字符的UTF-8编码与Unicode编码之间的转换关系对应下列规则:
\u0001和\u007f之间的字符,UTF-8编码为:
(byte)c.
\u0000或其范围在\u0080和\u07ff之间的字符,UTF-8编码为:
(byte)(0xc0|(0x1f&(c>>6))),(byte)(0x80|(0x3f&c))
\u0800和\uffff之间的字符,UTF-8编码为:
(byte)(0xe0|0x0f&(c>>12)))),(byte)(0x80|(0x3f&(c>>6))),(byte)(0x80|(0x3f&c))
从上可以看出,应用程序软件很容易根据UTF-8编码中那些固定不变的比特值来确定一个字符 占用的是一个字节呢,还是两个或是三个字节的,如果一个字节的第一个比特位为“0”,那么说明这个字符只占用一个字节;如果一个字节的前三个比特为 “110”,这说明这个字符占用两个字节;如果一个字节的前四个比特为“1110”,这说明这个字符占用三个字节。对于需要二个或三个字节表示的UTF- 8字符,它们的第二个和第三个字节的前两个比特位总是“10”。这样很容易与UTF-8中只占用一个字节的字符相区分,非常便于应用程序检测数据在传输过 程中是不是发生了错误。
UTF-8总结,ASCII码字符保持原样,仍然只占用一个字节,对于其它国家的字符,UTF-8使用两个或三个字节来表示,这对于中日韩文字来说明显增加了存储空间需求。使用UTF-8编码的文件,通常都要用EF BB BF作为文件开头的三个字节数据。
UTF-16:以16位为单元对UCS进行编码。对于小于0x10000的UCS 码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者 UCS4的BMP?必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却 要用于实际的传输,所以就不得不考虑字节序的问题。即Little endian和Big endian,第一个字节在前,就是”大头方式“(Big endian)FEFF,第二个字节在前就是”小头方式“(Little endian)FFFE。在intel架构的系统中默认是little endian。