编码与解码详细探究,找到错误的根源

概念

文本,字符串:显示在屏幕,控制台,网页等等的Unicode字符.
字符集:为显示的字符集合分配一个独一无二的位置
字节流:存放在硬盘,磁盘,传输流中的字节数据形式
编码:encode,是将文本、字符串转换成字节流的过程
解码:decode,是将字节流转换成文本,字符串的过程
编码与解码是一个互逆过程

像字符’A’,显示在屏幕中就是A本身,但是由于计算机是二进制的,只能存取0和1(但是读写按照字节读取),所以要将’A’保存到硬盘,要按照给定的字符集(默认Unicode)的位置’\x41’,使用"UTF-8"编码为0100 0001(2)存入硬盘,也就是字节流b’A’,

>>>	'\x41' #Unicode字符集的位置
'A'
>>> bin(ord('\x41'))
'0b1000001' #二进制位置
>>> 'A'.encode("utf-8")
b'A' #字节流
>>>	char='我'
>>>	char.encode('utf-8')
>>>	char.encode('gbk')
b'\xe6\x88\x91'
b'\xce\xd2'

在内存中暂存的所有的字符,都是Unicode编码,但只有往硬盘保存或者基于网络传输时,根据不同的编码规则,才能确定你输入的字符是英文还好汉文,这就是Unicode转换成其他编码格式的过程。

“好”.encode(‘ASCII’)
UnicodeEncodeError: ‘ascii’ codec can’t encode character ‘\u597d’ in position 0: ordinal not in range(128)

》》错误 .write方法默认写入字符串,不是byte数组
f.write(res.text.encode(‘utf-8’))
TypeError: write() argument must be str, not bytes
文件写入时,文件自动将unicode转为字节流存入文件中

encode(utf-8)将unicode转为字节流,所以不能将字节流直接存入 要将文件打开方式设置为wb+ 二进制模式才能写入

TypeError: a bytes-like object is required, not ‘str’。(倒过来 如果打开文件时是字节流 那么就不能写入str 而应该写入bytes 数组)

我们知道,GBK编码是一种单双字节变长编码,英文使用单字节编码,中文部分采用双字节编码
而UTF-8编码也是一种可变长编码,英文使用单字节编码,中文部分采用三字节编码.

例如:

char = '好'
print(char.encode('utf-8'))
print(char.encode('gbk'))
#输出
b'\xe5\xa5\xbd' #得到字节流
b'\xba\xc3'

注意:使用str.encode函数作用是将Unicode字符编码为字节流
因此,gbk编码器在编码 \xe5\xa5\xbd\ 中,只编码单字节 \xe5,或者 \xe5\xa5,留下的字节 \xa5\xbd或者 \xbd 不能编码为gbk字符集中的字符,所以报错.

char = b'\xba\xc3'.decode('gbk') #将字节流编码为gbk字符
print(char)
char = b'\xe5\xa5\xbd'.decode('utf-8') #将字节流编码为utf-8字符
print(char) 
#输出
好
好
#

```encode是一个逆向过程
"是".encode("gb2312")
b'\xca\xc7'   原本是字节流 将字节流当作字符来encode
b'\xca\xc7'.decode("gb2312")
'是'

### 思考与解决方案
所以为什么会出现这样的错误?
思考:**gbk编码器无法对字符'\xca'进行编码**,所以将页面的源代码```res.text```保存到新文件```get.html```中,推测源代码```res.text```使用UTF-8编码,而文件```get.html```使用GBK编码打开了
解决:将源代码和保存的文件都保持一致的编码
```python
#解决方案1,源代码保持utf-8,文件以utf-8编码打开
with open(r'd:\get.html', 'w', encoding='utf-8') as f:
    f.write(res.text)
#解决方案2,文件保持gbk编码打开,源代码以gbk重新编码
with open(r'd:\get.html', 'wb') as f:
    f.write(res.text.encode('gbk')) #显然人类在重复着相同的错误   

源代码res.text已经是utf-8形式了,所以gbk编码器依旧无法编码,
于是我们注意到,res.encoding可以改变编码res.text的输出方式

print(res.encoding)
#输出
ISO-8859-1 #一个被utf-8兼容的编码
#解决方案
res = requests.get('http://weibo.com')
res.encoding='gbk'
with open(r'd:\get.html', 'w') as f:
    f.write(res.text)

关于网页的编码问题

在上述的解决方案1中,虽然解决了编码错误,但是保存的文件get.html中,出现了乱码的问题:
在这里插入图片描述
因为源网页根本不是以utf-8编码,所以抓取得到的res.text如果默认用utf-8编码自然会乱码.
所以抓取网页时能确保不会出现乱码核心是: 保持编码一致
编码为X的源网页res=request.get->编码为X的源代码res.text->设置输出的编码为Xre.encoding->保存到编码为X的文件get.html
在网页的头部<head>中包含的元信息的标签<meta>记录了该网页的字符集charset,只要根据charset设置res.encoding以及保存文件的encoding=''就能保证编码一致

<meta http-equiv="Content-type" content="text/html; charset=gb2312"/>
import re
re.search('charset=(.*?)[;"]',res.content).group(1) #使用正则表达式提取字符集
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值