乱七八遭的编码总结~

前阵子写爬虫时被编码问题搞得好惨,然后就抽空好好看了一下,把总结的po上来吧


unicode是一种字符编码,让每个字符和一个数字对应起来,仅此而已,至于这个数字如何存储它就不管了。

utf8就是定义了如何具体存储这个编码数字的一种方法。


不过2003年11月UTF-8被RFC 3629重新规范,只能使用原来Unicode定义的区域,

U+0000到U+10FFFF,也就是说最多四个字节。utf-8不可以表示任意长度的字节


unicode是一个字符集,里面几乎包含了目前世界上已知的所有字符,

且该字符集将二进制代码和字符形成一一映射

即一个字符对应且只对应一个二进制代码,反过来,一个二进制代码对应且只对应一个字符。


Unicode字符集中对字符的编码是长度不确定的

其中有的字符是两个字符,有的是三个字符,这给计算机进行解码带来了困难

所以我们想给每个字符对应的二进制码前加上一个标记,让计算机看到这个标记就知道它将要读取几个字节,这样就防止了上述描述的两个字符因为前两个字节编码相同而误将三个字节的字符当成两个字节的字符读取了这种错误

那么这个标记该怎么加呢?

这就导致人们引入UTF-8,它的英文全称是8-bit Unicode Transformation Format,

可以看出它是将原本的Unicode码进行了transformation,这种transformation 就是给每个Unicode码进行标记,

使得让计算机看到某个标记就知道待会要读取几个字节的代码,从而避免问题发生。


情境如下:如果我们假设Unicode字符集有两个字符,
比如说(虚构了两个,只是打个比方):

1100 1111 1111 0001 1111 0101 对应字符‘我’,一共三个字节,

1100 1111 1111 0001对应字符‘你’,一共两个字节,恰好是‘我’这个字符二进制码的前两个字节。

如果我们存的是‘我’即三个字节的那个代码,即1100 1111 1111 0001 1111 0101,

然后让计算机进行解码时,它会从左向右依次读取一个数码,当读到1100 1111 1111 0001时它可能就停止

让‘你’这个字符与其对应,并不是我们当初想存的‘我’这个字符,只是因为这两个字符对应的二进制码前两个字

节是一样的,这就是一个问题。


UCS沿用传统的做法,将文本严格按照unicode字符集编码表存储和传输,

如UCS-2采用16位两个字节表示一个字符

UCS-4采用32位二进制四个字节表示一个字符,这样的方案没有得到推广。

因为,对于一个字节就可以满足需求的英语文本来说,UCS的方案却统一采用两个字节甚至四个字节来处理,使文件的体积大了至少一倍,不仅浪费了硬盘的空间,也降低了网络传输的效率;

此外,还有公司搞出unicode big endian和unicode little endian,使unicode的编码方案更加复杂。

这时候,终于有人提出,字符集的编码表和计算机的数字编码是可以不同的,

他们制定了UTF (UCS Transfer Format)方案,将unicode的字符集编码按照UTF的规则进行重新编排

如UTF-8以8位为一个单元,UTF-16以16位为一个单元等等。

utf8是对unicode字符集进行编码的一种编码方式。

Unicode is charset。
UTF-8 is encoding。


ASCII码跟Unicode没有本质的区别。只不过Unicode表示范围比ASCII大。

ASCII可以表示127个英文字母,其中每个英文字母都有一个十进制编码,并且通过这个十进制编码转化成二进制数(编码)存入到内存当中(占1字节)。

而在Unicode中,英文字母的编码与其在ASCII中没有不同。

只是Unicode每个字符占2个字节,于是转化为二进制时就变成‘000000 ASCII’。

同时Unicode支持中文字符以及其他字符的表示。这种非ASCII字符占用的内存更多一些。

而UTF-8则是在Unicode的基础上进行的再编码。其中对于字母的编码与Unicode和ASCII一致。

文本文档是用UTF-8编码保存的。这样可以最大限度的节省空间。

但是当你对这个文本文档进行操作的时候,计算机是先把UTF-8转化为Unicode然后放到内存中,让用户进行操作,操作完成后在内存中还是Unicode模式。

当你需要保存的时候再转化为UTF-8格式保存(节省空间)。


我们不客气 地把那些127号之后的奇异符号们直接取消掉, 规定:

一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,

前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。

在这些编码里,我们还把数学符号、罗马希腊的 字母、日文的假名们都编进去了,

连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码

这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。 

中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。

GB2312 是对 ASCII 的中文扩展。


但是中国的汉字太多了,于是干脆不再要求低字节一定是127号之后的内码,

只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字 符集里的内容。

结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字

(包括繁体字)和符号。 

后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。

从此之后,中华民族的文化就可以在计算机时代中传承了。 

中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “DBCS“(Double Byte Charecter Set 双字节字符集)。

在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里

因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。

那时候凡是受过加持,会编程的计算机僧侣 们都要每天念下面这个咒语数百遍: 

一个汉字算两个英文字符!一个汉字算两个英文字符……”


一个叫 ISO (国际标谁化组织)的国际组织决定着手解决这个问题。
他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号 的编码!
他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“


unicode开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。

于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,

unicode包持其原编码不变,只是将其长度由原 来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。

由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在 保存英文文本时会多浪费一倍的空间。


一个汉字不再是相当于两个字符了,而是一个!
是的,从unicode开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的”一个字符“!
同时,也都是统一的”两个字节“,请注意”字符”和”字节”两个术语的不同,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。
在unicode中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。


UTF-8最大的一个特点,就是它是一种变长的编码方式。

它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,

当字符在ASCII 码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,

注意的是unicode一个中文字符占2个字节,而UTF-8一个中 文字符占3个字节

从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换。


举一个例子:It's 知乎日报

你看到的unicode字符集是这样的编码表:
I 0049
t 0074
' 0027
s 0073
  0020
知 77e5
乎 4e4e
日 65e5
报 62a5
每一个字符对应一个十六进制数字。


计算机只懂二进制,因此,严格按照unicode的方式(UCS-2),应该这样存储:
I 00000000 01001001
t 00000000 01110100
' 00000000 00100111
s 00000000 01110011
  00000000 00100000
知 01110111 11100101
乎 01001110 01001110
日 01100101 11100101
报 01100010 10100101

这个字符串总共占用了18个字节,但是对比中英文的二进制码,可以发现,英文前9位都是0!

浪费啊,浪费硬盘,浪费流量。

怎么办?

UTF。

UTF-8是这样做的:

1. 单字节的字符,字节的第一位设为0,对于英语文本,UTF-8码只占用一个字节,和ASCII码完全相同

2. n个字节的字符(n>1),第一个字节的前n位设为1,第n+1位设为0,后面字节的前两位都设为10,这n个字节的其余空位填充该字符unicode码,高位用0补足。

这样就形成了如下的UTF-8标记位:

0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx


于是,”It's 知乎日报“就变成了:
I 01001001
t 01110100
' 00100111
s 01110011
  00100000
知 11100111 10011111 10100101
乎 11100100 10111001 10001110
日 11100110 10010111 10100101
报 11100110 10001010 10100101

unicode是信源编码,对字符集数字化;
utf8是信道编码,为更好的存储和传输。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值