订阅内容解码失败(非base64码)_编码、解码

本文通过Python2和Python3的实验,详细解释了编码和解码的概念,以及Bytes和Unicode类型的区别。在Python2中,未声明编码的代码默认以ASCII加载,可能导致非ASCII字符的解码错误。通过`#coding: utf-8`声明可以解决这个问题。而在Python3中,代码默认以Unicode加载,打印机制直接显示对应字符。文中通过实例展示了编码、解码的过程,并探讨了不同类型字符在内存中的表示形式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以Python2和Python3为例:

1f83f1df2e57263980119bc624f262d6.png
图1 有点懵

在说编码、解码的概念之前,我们先说一下,写代码执行代码的区别.

以Python2实验

1. 写代码

如果我说,我们写的代码首先是以二进制01的形式存储在硬盘中的,你可能会认为我说的就是废话,不管是何种形式的编码规则(ASCII编码、GB2312编码、GBK编码、FB18030编码、UTF-8编码)对字符进行编码,最后在计算机硬盘中存放的形式都是01形式,关键是以什么样的方式(编码)变成二进制,如果你真的这样想,那么你是对的,哈哈.(注:后面都用十六进制来表示在硬盘和内存中的存放形式,这样比较容易观察.)

如果你用Pychram软件编写的Python代码,默认是以UTF-8编码形成的01形式存放在硬盘中,VS code亦是如此,如图2所示:

669d074d67eeecab5eeeaa3824d14cd3.png
图2 VS code界面

2. 运行代码

那运行代码又是怎么回事呢?

239be3251a07602e5f429712864a9d24.png
图3 写代码与运行代码

前面我们说过,在用Pychram软件写Python代码是以UTF-8编码形成的01形式存储在硬盘,如果用Python2运行代码,就会将代码转换成ASCII码加载进内存,如果用Python3运行代码,就会将代码转换成Unicode的形式加载进内存.

我们利用python2来写一个实验:

a = 'hello'
print a

输出结果:hello

a9fbcda0fec33222a412f3f54c4fae4b.png
图4 print a

通过Pychram运行代码没有任何问题,那么,这段代码从运行结束之后,到底发生了一个怎样的过程呢?

c36d698c15263a68511982be0cbdeaae.png
图5 执行代码

分析:如图5所示,"hello"字符串先以UTF-8码存储在硬盘中(这个编码规则是取决于编写代码的软件,这里用的是Pycharm),然后UTF-8码转换为ASCII码加载进内存(各种编码只要有对方的字符,都有一个转换公式,这个不要太过纠结,还有一点说明,UTF-8码是向下兼容ASCII码).

可能有人要问了,你怎么通过这个例子就能证明是转换成ASCII码加载进内存的呢?说的很好,确实不能证明,仔细观看图5就会发现,加载进内存中的编码与硬盘中的编码都是x68x656c6f,在内存中也完全有可能是UTF-8码,根本就没有发生变化.

既然如此,我们换个打印对象,换成'你好'这个字符串.

实验:

a = '你好'
print a

输出结果出错(SytaxError: Non-ASCII character 'xe4' in file...)

2e56effcaa8e5867391c0cedc1fe87fb.png
图6 执行代码

查错时,你就会发现:没有 ascii码:"xe4"

那么这个代码从写到运行结束又经历了一个怎样的历程呢?

d0c26d8c4731420bdd0eaa41c2aa493e.png
图6 print a

分析:首先,代码以UTF-8码存储在硬盘中,'你好'这个字符串每个字由3个字节表示,共由6个字节表示,点击代码运行按钮,代码由UTF-8码转换成ASCII码加载进内存时,发现'xe4'(十六进制),超过ASCII码的范围,所以报错. (ASCII码范围是0~127,转换为十六进制:0~7F,xe4明显大于7F,不能进行编码转换.)

那么问题来了,难道Python2就不能打印中文吗?

我们再进行实验,如图7所示:

代码如下:

#coding : utf-8
a = '你好'
print a 

0a52c8ac1143919f8d9cfcdc5cb6f2eb.png
图7 print a

现在又能正常打印中文了,发生了什么?

首先观察代码,就会发现,本实验就比上个实验多了一行代码:#coding : utf-8

这行代码的意思就是:声明此文本代码的编码规则为utf-8 (加载进内存的编码规则)

可能有人听到这,还是有点懵...

我们再来观察本实验代码从写到运行结束的过程,如图8所示:

1bae30bd3ad720ade36a21a86ddc21a0.png
图8 print a

首先代码以UTF-8码存放在硬盘,然后加载进内存,注意此时是转换成UTF-8码加载进内存的,因为我们在写代码时声明:#coding:utf-8 (UTF-8 与utf-8是一样的,不要纠结大小写)

可能有人问了,UTF-8表示一个汉字是三个字节,我想用GB2312编码行不?

话不多说,实验是检测真理的唯一标准.

#coding: gb2312
a = '你好'
print a

871ff5617db2c988d707ffac6152d73f.png
图9 print a

what,发生了什么,怎么会这样子?

虽然没有报错,但打出来的是啥东东,打印了三个 ?...

b13422d518ba4376c28559e78d907cc0.png
图10 print a

猜想:这可能跟Python2的print机制有关,Python2诞生的年份比较早,那时候没有完全考虑到中文,没有编码声明,就会将print后面的内容当作ASCII码显示对应的字符,如果有编码声明,且编码声明是UTF-8,也会显示相应的字符,但如果编码是GB2312或GBK,那就不会显示相应的字符. (注:猜想不一定正确哦,借鉴即可)

到了这里,我们需要引申一个概念:Bytes类型

3. Bytes类型

什么是Bytes类型?

前面我们知道,默认存放在硬盘的是UTF-8编码,而加载进内存时,可以是各种编码(ASCII码、UTF-8码、GB2312码 等等),无论它是以何种编码加载进内存的,加载进内存的内容肯定是一种编码. 我们称以某种编码加载进内存的数据类型为:Bytes类型.

Python2当中,并不叫Bytes类型,而是称为str类型.(大家不要纠结名称,我们知道它的本质其实是Bytes就行了)

f32548648127fc16191051e584641762.png
图11 type(a)与repr(a)

如图11,type():是python查看数据类型的内置函数,

repr(a):一种机制,可查看数据在内存中的码值,如果repr后面是ASCII码,默认把对应的字符显示出来.

为了更好观察,我们打印汉字(注:汉字的编码不在ASCII码中)

代码:

#coding: utf-8
a = '你好'
print type(a)
print repr(a)

b56819d60fd3e93315a9adf21758f87c.png
图12 type(a) 与repr(a)

打印出来:'xe4'xbdxa0xe5xa5adb' (注:Bytes类型)

可能有人问了,加载进内存的都是Bytes类型吗?

继续往下读了,你就明白了...

4. Unicode类型

我们先来看一段代码,如图13所示:

992b65426d554e341b50d88c12605d77.png
图13 print a

有人可能问,'你好'前面的u是什么意思?

u:告诉python2解释器运行这段代码时,要采用Unicode形式(UCS-2字符集:码位的二进制映射)把u后面的内容,加载进内存.

Python2也将这种数据类型称为Unicode.

405b71a1da5759fe93994c62af2559ef.png
图13 Unicode类型

现在我们知道了,加载进内存的代码有两种类型:① Bytes; ② Unicode

可能到这,有人又要问了,这两种类型有联系呢?

aa788b1548854d70d7c7ebd29b9bb983.png
图14 Bytes和Unicode

Unicode其实是一种对字符码位的二进制映射,不属于严格意义上的编码. (如果不理解,就去看上一篇文章或相关的链接视频). 一个理解为熟饭(Bytes),一个是生米(Unicode).

0704:字符集、编码​zhuanlan.zhihu.com

f28c751a5c883fd71ea52ffffb949dbe.png
图15 编码

3c3b02b46ee07e7018672225b9bb6ce1.png
图16 解码

其实很好理解:

生米 → 熟饭 : 编码

熟饭 → 生米 : 解码

注:编、解码不仅是属于Python2或Python3,而是适用于任何的编程语言,为通用概念.

说了这么多,我们来做一个实验.

3295f6ac3f920c0efab4d0a389065d97.png
图17 英文字符解码

如图17:我们知道'hello'在内存中的默认形式是ASCII码(Bytes类型),我们对它进行解码,就可以看到它的类型在内存中变成了 Unicode类型.

12502f82cf344039ea77e699065bd940.png
图18 查看编码值

如图18所示:我们利用repr()函数,想查看'hello'的Unicode码(其实就是Unicode码位的二进制的映射),但结果好像不是值,而是字符.

这是咋回事呢?

其实前面我也说过了,这是Python2的显示机制,如果是英文,显示出的是对应Unicode码的字符.

我们改用汉字实验一下:

a9e53b053be95432dc4643d63e9d7f34.png
图19 汉字解码

结果又出现问题,是不是有点崩溃,哈哈,我也是. (不过不要急,在解决问题过程中,你会学习到很多知识)

分析:首先看代码,'你好'以UTF-8码加载进内存(Bytes类型),现在想解码成Unicode类型,如果我用ASCII码进行解码,仔细一想,肯定会出现问题,'你好'的UTF-8码没有对应的ASCII码,而你用ASCII码去解码肯定会有问题的;如果用UTF-8码进行解码,当然是没有问题

所以我把 print a.decode('ascii')去掉,如图20所示:

d6d1cac06799219606743f620d25deb1.png
图20 汉字解码

结果:print a.decode('gb2312') 这段代码出现问题.

分析:因为 UTF-8码的十六进制在GB2312字符集中无法找到相匹配的字符。

101ca33eb3caa8e196b6d64dab946881.png
图20 汉字解码

cdda61a9b47dd945bb2aa4212bb4c234.png
图21 Unicode码

c6dd2052cf87041841350e3de92669a4.png
图22 总结

以Python3 实验:

c3d2bec2eb22734a4ee0fd596310b61c.png
图23 unicode 与 str

Python3 的print机制会将print后的Unicode码,显示出相对应的字符,而那些不是Unicode码,也会原封不动的显示出来.,如图23所示,第三个打印的是こんにちは(日语:你好)(注:Python3默认加载进内存的是Unicode类型)

Python3将这种Unicode类型称为:str类型(额,搞来搞去,也是有点醉)

其实大家知道它的本质是:以Unicode类型存放在内存中就行.

93e587309992d079e2fa0c6f6a02f54f.png
图24 str类型

那么有人又想问了,既然有Unicode类型,是不是也应该有Bytes,确实这样.

我们可以定义一个Bytes类型,如图24所示:

5db39dfd2353e27b91f6d3c1106726b6.png
图25 创建Bytes类型

观察打印结果,发现原封不动的打印出来了.

这又是怎么回事呢?

其实我们刚才说过,Python打印时,如果是Unicode码(内存里存放的形式),会打印相对应的字符,如果不是Unicode码,会原封不动的显示出来. 图24所示的代码,加载进内存的是:Bytes类型,而非Unicode类型(对应的就是Unicode码),所以会原封不动的显示出来.

其实图24所示的代码发生了如下过程:

5f896698a417c2daecf9efdae3be4422.png
图26 代码执行过程

分析:b'xe4xbdxa0xe5xa5xbd'先以UTF-8的形式写进内存,这里注意:是引号里每个字符对应的UTF-8码(包括xe4...),Python解释器看到字符串前有个b,就会以某种编码转换成我们所理解的Bytes类型,而Python3的print只会对Unicode类型显示相应的字符,其它的原封不动显示出,所以最后呈现出的还是b'xe4xbdxa0xe5xa5xbd' .

既然,Python3默认在内存中加载的是Unicode类(生米),那我们就可以进行编码,如图27所示:

bf8c0b3268b90fa9c55d3b4c316e68e2.png
图27 英文字符的编码

bab4eb6faa24702a3afaa0e6402466aa.png
图29 汉字的编码

解码的过程与编码相反:

5cd3ba9c635f75d5029fc5854d8b33c7.png
图30 解码

Bytes类(UTF-8码)加载进内存,因此采用UTF-8解码成Unicode类,Python3会显示对应的字符.

说到这里,差不多结束了,大家有没有感觉有点懵...

39b3cd9e2095f9b15074b674c5ed0249.png
图31 总结
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值