详解计算机编码

关与作者更多博客请访问云里云外开源社区

详解计算机Unicode编码

字符时是如何在计算机中表示的呢?众所周知,不论是数值、文本、图形图像、音频视频在计算机中都是以010101的二进制数字表示的,所以01的二进制与信息肯定存在一种对应关系,这种对应关系就称为编码。

百度词条的解释是“编码是信息按照约定的规则从一种形式或格式转换为另一种形式的过程”。也就是将字符变成计算机存储器中的高低电平。

编码的发展从古时就已经出现,古人结绳记事就可以算为对编码的一种应用。我们当然可以每个人或组织都设计一套自己的编码规则,但这样不利于计算机的普及和信息的交流。

于是一些常用的计算机编码就应运而生了。

1.ASCII(American Standard Code for Information Interchange)

美国信息交换标准代码,简称美标。最初是由美国国家标准协会(ANSI)指定的,后被国际标准化组织(ISO)定位国际标准,称为ISO 646标准。

标准ASCII最多只能表示128个字符,最高位默认为0,用来作奇偶校验位。

所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1

特点:

  1. 英文、数字按顺序排列
  2. 可以根据一个已知的英文字符位置推出其他的英文字符位置

点击查看源网页

2.信息时代的‘书同文,字同码’ Unicode

ASCII并没有考虑到不同语言文字的编码解决方案,所以各个国家和地区都诞生出自己的编码方案,如中国、新加坡使用的GB2312-80、香港和台湾使用的BIG5。但如果没有一个统一的编码方式,在使用其他国家提供的信息服务时就会遇到很多不便,例如访问网站出现的乱码等。

所以国际标准化组织就对世界上各主要语言建立了统一的编码系统—Unicode编码系统。

Unicode:通常都是用到的是两个字节,0x0000-0xFFFF之间也就是65536以内。所以支持多语言,一般英文字母的ASCII转换为Unicode只需在最高位补0即可

字母A用ASCII编码是十进制的65,二进制的01000001;

用Unicode编码:00000000 01000001

Unicode编码系统主要包括编码方式实现方式两个层次。

编码方式可以理解为一本超大字典,将地球上主要文字都记录其中,每一个文字都对应一个编号。这个编号称为代码点(code point),其值通常写为U+ABCD的形式。文字和代码点之间的对应关系就是UCS(Universal Character Set coded in 2 octets),有UCS-2、UCS-4。

UCS-2

用两个字节表示代码点,如果这本字典一页保存一个字符,那最多有2^16即65535页。UCS-4同理,就是为了表示更多的字符而产生的。

那么UTF-8、UTF-16、UTF-32又是什么呢?

UCS只规定了字符和代码点之间的对应关系,并没有规定代码点在计算机中如何存储,规定存储的方式称为UTF(Unicode Transformation Format),也就是实现方式

UTF-16

它使用两个或四个字节来表示一个代码点,也属于变长表示。Unicode这本大字典不是一次性定义的,而是分区定义。每个区可以存放65536个字符,一个区称为一个平面,目前一共有17个平面。第一个平面(U+0000-U+FFFF)为基本平面(BMP),所有最常用字符都放在这个平面。其余字符放在辅助平面(SMP)更详细的案例请浏览 java原生就是用的UTF-16

然后通过三种方式存储在计算机中;UTF-16,UTF-16BE(Big Endian),UTF-16LE(Little Endian)

Big Endian和Little Endian如果没记错的话叫大、小尾端。通俗的说就是字节在计算机存储器中的存放是从高位字节开始还是从低位字节开始存储。

例如0xabcd这个16进制的信息存储在计算机中,0xab是高位,0xcd是低位,大尾端就是先存储高位,再存储低位;小尾端反之亦然。(记忆方式:首先明确存储是从内存小地址到大地址存储。Big表示先将信息中高位的数据存入内存)

addressbig endianlittle endian
0x00000xab0xcd
0x00010xcd0xab

至于UTF-16的存储方式,则是在文本文件遵循UCS规范,先传输名为BOM的字符 “Zero Width No-Break Space”。这样如果接收者收到 FEFF,就表明这个字节流是 Big-Endian 的;如果收到FFFE,就表明这个字节流是 Little- Endian 的。

“ABC”这三个字符用各种方式编码后的结果如下:

UTF-16BE00 41 00 42 00 43
UTF-16LE41 00 42 00 43 00
UTF-16(Big Endian)FE FF 00 41 00 42 00 43
UTF-16(Little Endian)FF FE 41 00 42 00 43 00
UTF-16(不带BOM)00 41 00 42 00 43

UTF-32

用四个字节表示一个代码点,UTF-32也包括UTF-32、UTF-32BE、UTF-32LE三种编码,UTF-32也同样需要BOM字符。

仅用’ABC’举例:

UTF-32BE00 00 00 41 00 00 00 42 00 00 00 43
UTF-32LE41 00 00 00 42 00 00 00 43 00 00 00
UTF-32(Big Endian)00 00 FE FF 00 00 00 41 00 00 00 42 00 00 00 43
UTF-32(Little Endian)FF FE 00 00 41 00 00 00 42 00 00 00 43 00 00 00
UTF-32(不带BOM)00 00 00 41 00 00 00 42 00 00 00 43

UTF-8

utf-8没有固定使用几字节表示代码点,因此可以很好的节约空间。

ASCII:一个字节,所以最多只能表示256个字符。但最高位默认是0,所以ASCII实际上最多能表示128个字符,只支持英文

UTF-8:变长编码,支持多语言,节省空间

编码规则如下:

  1. 对于单个字节的字符,第一位设为 0,后面的 7 位对应这个字符的 Unicode 码点。因此,对于英文中的 0 - 127 号字符,与 ASCII 码完全相同。这意味着 ASCII 码那个年代的文档用 UTF-8 编码打开完全没有问题。
  2. 对于需要使用 N 个字节来表示的字符(N > 1),第一个字节的前 N 位都设为 1,第 N + 1 位设为0,剩余的 N - 1 个字节的前两位都设位 10,剩下的二进制位则使用这个字符的 Unicode 码点来填充。

编码规则如下:

Unicode 十六进制码点范围UTF-8 二进制
0000 0000 - 0000 007F0xxxxxxx
0000 0080 - 0000 07FF110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 0010 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

根据上面编码规则对照表,进行 UTF-8 编码和解码就简单多了。下面以汉字“汉”为利,具体说明如何进行 UTF-8 编码和解码。

“汉”的 Unicode 码点是 0x6c49(110 1100 0100 1001),通过上面的对照表可以发现,0x0000 6c49 位于第三行的范围,那么得出其格式为 1110xxxx 10xxxxxx 10xxxxxx。接着,从“汉”的二进制数最后一位开始,从后向前依次填充对应格式中的 x,多出的 x 用 0 补上。这样,就得到了“汉”的 UTF-8 编码为 11100110 10110001 10001001,转换成十六进制就是 0xE6 0xB7 0x89

解码的过程也十分简单:如果一个字节的第一位是 0 ,则说明这个字节对应一个字符;如果一个字节的第一位1,那么连续有多少个 1,就表示该字符占用多少个字节。

小结:ASCII作为开山鼻祖,用一个字节表示英文、数字、特殊字符在内的128个字符,但其他国家和民族的文字用128个字符无论如何是无法表达的,所以Unicode应运而生,开始UCS-2,即65536个字符,这个范围能表达大多数常用字符;还有些生僻字用到了UCS-4,至此Unicode编码将主流文字集于一身。在使用方便的同时带来内存空间浪费的问题,UTF-8的可变长的存储方式诞生了。从英文的一个字节,到中文常用字的三个字节,再到生僻字的六个字节。

3.python中关与编码的应用

python3中默认支持utf-8编码

>>> import sys
>>> sys.getdefaultencoding()
'utf-8'

我们可以通过encode()和decode()验证这一点

>>> '中国'.encode()
b'\xe4\xb8\xad\xe5\x9b\xbd'
>>> '中国'.encode('utf-8')
b'\xe4\xb8\xad\xe5\x9b\xbd'
>>> b'\xe4\xb8\xad\xe5\x9b\xbd'.decode()
'中国'
>>> b'\xe4\xb8\xad\xe5\x9b\xbd'.decode('utf-8')
'中国'

这里要注意:\x标书16进制,字符串前加b表示字符串是bytes类型

将unicode转为utf-8

>>> '中'.encode('unicode-escape')
b'\\u4e2d'
>>> '中'.encode('utf-8')
b'\xe4\xb8\xad'
>>> test = b'\\u4e2d'
>>> test.decode('unicode-escape').encode()
b'\xe4\xb8\xad'
>>> test.decode('utf-8')
'\\u4e2d'
>>> test.decode()
'\\u4e2d'
>>> test.decode('unicode-escaoe')
Traceback (most recent call last):
  File "<pyshell#56>", line 1, in <module>
    test.decode('unicode-escaoe')
LookupError: unknown encoding: unicode-escaoe
>>> test.decode('unicode-escape')
'中'

可以看出,encode()默认是用utf-8编码,编码后的结果也只能用utf-8进行解码。

Chr():character ,将编码转换为对应的字符,有效范围是0-0x10FFFF,其他范围的值会抛出异常ValueError

Ord():ordinal序数,获取字符的Unicode编码(10进制),通过hex()转换为16进制

>>> ord('中')
20013
>>> hex(ord('中'))
'0x4e2d'

因为python的诞生时间比Unicode要早,所以python早期版本支持ASCII,Python3也开始支持Unicode

参考文章

大小尾端

编码

python编码应用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值