字符编码

如果非要评个计算机初学者最头晕概念的话,估计字符编码可以排到前十,比马尔可夫链之类的理论性概念都要晕。为什么呢?因为这东西课本上不会讲,也没有什么理论,只能靠自己体会出来。我比较笨一点,晕了好几年,到了今年终于明白了怎么一回事,现在记在这边,免得再忘了。
首先定义只在本文中出现的两个基础概念:
1.字节组。每8个位(bit)是为一个字节,多个字节成为一个字节组。
2.文字串。现实语言的最小单位称为文字。一个英文字母就是一个文字,一个汉字也是一个文字,一个空格也是文字。
这两个概念是我生造的,主要是为了避免和现有的一大堆术语混起来。
文 字串不能理解成字符串。倒是像圆这样的数学概念,比较抽象一些,只存在于头脑里。可以把文字串写在纸上,也可以记在计算机里,还可以读出来。字节组就是文 字串在计算机中的表现形式。想一想,在纸上圆可以用一个公式来表达,也可以用一个图形来表达,还可以用文字描述它。文字串也一样,可以表述为多种字节组。 众所周知,计算机只能处理数字,所以为了描述文字,最简单的方法就是把文字列出来,每个文字用一个数字表示。不同的国家有不同的方法来做这件事,比如美国 人和英国人只有字母,还有一些符号,总共排了256个文字,叫做ascii码。中国的文字就比较多了 ,那个康熙大字典据说有40万,所以中国人排了gb2312标准,big5,gbk、还有gb18030,里面包含的文字总数各不相同。后面几个名词很熟 悉吧。
3.把文字串表述成字节组的方法就叫做字符编码。
显然,美国人的ascii码是不能表示中国文字的,因为它只能表示256个文字。 诚然,中国的标准可以表示美国人的文字,但是却不能表示阿拉伯字符和其它一些像型文字。后来大家意识到这个问题是文化交流的障碍,于是就在一起制定了一个 unicode标准。简而言之,unicode标准就是穷尽这个世界上所有的文字,给每个文字编一个数字。 和gb2312,gbk等一样,其实unicode也只是一种编码标准,只不过它能够表示所有的文字。从此,如果一个计算机系统想要支持多种语言文字,只 要简单地支持unicode就可以了。在这种系统里,我们可以认为文字串就是unicocde字节组,unicode字节组就是文字串。
虽然解决 了编码不能相互表示的大问题,但是还有一个麻烦没有解决。就像前文所说的,所谓编码就是给文字编一个数字。在每个编码里,数字与文字是一一对应的,但是在 不同的编码标准里呢?不同的编码标准可能包含相同的文字,比如big5里的公和gbk里的公就是同一个文字,但是被编了不同的数字。这时候,同一个文字对 应了不同的数字。再者,同一个数字是否代表着同一个文字呢?显然不是的,不然,两种编码标准就只有包含文字多少的区别了,或者每个文字在两个编码对应了相 同的数字,那这还是两个编码标准吗?
我经常感叹,要是这个世界上只有unicode这种编码标准那该多好啊,我们就不用操心这些问题了。可惜事实 不是这样子的,以前的标准仍然盛行,特别是windows仍然使用旧的标准。在中文简体的windows上,用记事本写的文本文件仍然是gbk编码的。想 要在一个使用unicode的浏览器上显示出文字来仍然不行,不同的编码环境从文件里读取的数字不会变,但是却表示了不同的文字。怎么办呢?很容易想到的 办法是读取文件内容的时候把gbk编码转成unicode编码。仔细想一想,这应该是没问题的,因为gbk包含的文字只是unicode包含的文字的一部 分而已,从gbk转成unicode应该是没问题的。反之,从unicode转成gbk却不一定可以。
还有一种情况是两种编码标准差不多,只是每 个文字在两种编码标准里对应的数字不一样。比如gbk和cjk,两种编码标准都包含了大量的中文简体文字和繁体文字,但是两种编码是不一样的。一个gbk 的文本文件想拿到只支持cjk的环境里阅读要怎么办呢?也要在读取文件的时候从gbk编码转成cjk编码。目前一般采用先把gbk转成unicode,然 后再从unicode转成cjk的间接做法。这可不可行呢?前面我们已经知道了,gbk转成unicode是可以的,但是unicode的文字转成cjk 却不一定可以。不过这并不是什么大问题,因为如果真的不能从unicode转到cjk的话,说明那个文字cjk根本不能表示,直接从gbk转成cjk肯定 也不可以。与直接从gbk转成cjk相比,间接的转码多了一个步骤,不过形式上比较统一,只要每种编码都编写一段与unicode相互转换的程序,就可以 进行任意编码的相互转换。这个世界存在太多的编码标准,想要每两种可以转换的编码都可以相互转换需要的转换程序太多,试想想一个多边形的每两点相互连接。
再 进一步地考虑,虽然编码是一样的,但是数字转换成字节组的方式可不可以有多种方式呢?答案是,可以的。最典型的是unicode编码。unicode编了 很多个文字,个数总是在增加,目前(2008年7月)是100713个文字。把文字串转换成unicode字节组最简单的办法就是每个文字用三个字节来表 示,因为100713这个数字最少需要三个字节才能表示。不过实际上通常采用的是utf-8方案。其中,最常使用的128个英文字母用一个字节来表示,而 中文使用三个字节(注,我经常以为是两个字节,不过那是其它字符)来表示。还可以使用utf-16方案,其中英文和中文都使用两个字节来表示,而其它字符 采用四个字节。还有一种utf-32方案,所有的文字都用四个字节来表示。大多数软件在内存中使用utf-16来表示文字,而在硬盘文件或者网络数据流中 使用utf-8。前者字节数比较固定,比较容易处理,而后者比较节约空间。
讲到这里,相信大家对字符编码已经了解得差不多了吧。我们再来理论联系实际。
首 先看看python对于字符编码的处理。应该说python是*nix派系语言里对字符编码处理得比较好的了。它内建了对unicode的支持。 python2.x版本里有两种类型的字符串,一种称为str类型,实际上可以理解为字节组。还有一种是unicode类型,实际上是utf-16的字节 组,我们可以理解为文字串。
“中文” —> 这是str类型
u”中文” —-> 这是unicode类型
从文件、终 端、网络数据流或者其它外部输入中读取的都是str类型字节组,而且向文件、终端、网络也只能输出str类型的字节组。unicode类型的文字串在输出 到文件的过程中会自动转换成ascii编码,输出到终端的过程中会自动转换成系统默认的编码(简体中文windows为gbk)。str类型与 unicode类型可以使用decode和encode两个方法互相转换,比如在简体中文windows的命令行界面里:
“中文”.decode(’gbk’) == u”中文”
u”中文”.encode(’gbk’) == “中文”
想要从文件里读取gbk编码的数据然后使用utf-8编码发送到网络上可以这样:
buf=file(filename,”r”).read()
mysocket.send(buf.decode(’gbk’).encode(’utf-8′))
很显然,unicode文字串没有decode()方法,而str字节组没有encode()方法。
值得注意的是,python经常会在输出的时候自动把unicode文字串转换成str字节组,而在字符串连接的时候把str字节组转换成unicode文字串。其中有些规律我还不大懂,哪位大虾指导一下。
在以后版本的python里,表示方法略有不同:
“中文” —> 这是一个unicode字符串
b”中文” —> 这是一个str(python3k叫做bytes)类型的字节组
接 下来再以java为例。与python相比,java对于unicode的支持更胜一筹,因为存储在java程序文件或者数据文件里的字符串都是以 utf-16保存的,并且输入输出时,java能做更多的自动转换(比如列出一个文件夹里所有的文件)。不过基本原理跟python是一样的,它也有两种 类型,一种是byte[]类型,这是字节组。还有是String类型,这是unicode文字串。它们也是可以相互转换的:
String(”中文”).getBytes(”utf-8″) —-> 将unicode文字串转成utf-8字节组
new String(new byte[]{228, 184, 173, 230, 150, 135},”utf-8″) —–>将utf-8字节组转成unicode文字串
java还用输入输出专门提供了自动转换编码的类。InputStream和OutputStream专门用于输入输出byte[]字节组,而Reader和Writer专门用于输入输出unicode文字串。
最 后,再说明一下Qt对于字符编码的处理。 c和c++对于unicode的支持是比较差,幸好qt为此下了番苦功,整体来说还过得去。qt对于字符编码的处理基本上与java类型。 QByteArray(或者char[])类型是字节组,而QString是unicode文字串。它们之间的关系是这样的:
char[] data=char[6]{228, 184, 173, 230, 150, 135};
QTextCodec::codecForName(”utf-8″).toUnicode(data,6) —-> 将utf-8字节组转成unicode文字串
QTextCodec::codecForname(”utf-8″).fromUnicode(QString(”中文”)) —-> 将unicode文字串转成utf-8字节组
qt会自动地将c的char[]转换成unicode文字串。比如上文的QString(”中文”)。还有tr()函数,以及C++的隐式类型转换。自动转换使用的默认编码通常在QApplication构造后进行初始化,比如:
#ifdef Q_CC_MSVC
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale()); //utf-8? QTextCodec::setCodecForTr(QTextCodec::codecForLocale()); //utf-8?
#elif Q_CC_GNU
QTextCodec::setCodecForCStrings(QTextCodec::codecForName(”gbk”)); //与文件的编码同
QTextCodec::setCodecForTr(QTextCodec::codecForName(”gbk”)); //与文件的编码同
#endif
看起来很麻烦的样子,像java程序里”中文”就可以直接表示unicode文字串了,而qt还需要tr()转换一下。不过,幸好从utf-8字节组转换成unicode文字串还是比较简单的:
QString::fromUtf8(data); —-> 将utf-8字节组转成unicode文字串
tr(”中文”).toUtf8(); —-> 将unicode文字串转成utf-8字节组
如有不对,还请大牛们指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值