今天台风过境,风雨交加,突然就想写一下这个在学习编程初期,非常困扰的问题。
我会尽我所能将这个问题描述清楚,如果有不当之处还请指正!
转载请注明出处即可
字符与字节
字符 就是我们平常看得见的这些,比如 '1','c',"中文", 在程序代码中它们往往表现为一个字符串(其实字符的范围要广得多,还包括不可见的控制字符,后来出现的 emoj 字符等)
但是我们知道其实计算机内部都是只含 01 的二进制序列,于是又引出了两个概念
字节 ,比如 \x31, \xff,它们才是实际在计算机中存储的东西
而从字符到字节的映射,就是 字符编码
ASCII 编码
这是我们经常听到的编码,在 ASCII 编码中,可见字符 '1','2','a','f' 或者 控制字符 '\n','\b' 等都被编码为一个字节
其他编码
其他编码多种多样,比如 GBK,GB2312 等,它们可以表示汉字,将每个汉字编码为多个字节
所以也就出现了所谓 多字节字符 的概念
使用范围最广的应该是 UTF-8 编码,它用 1-6 的字节来编码世界上所有的字符,包括拉丁字符,中文字符,西洋字符,emoj 字符等等,几乎所有
编码报错
了解了这些就能理解为什么 编码出错,解码出错 等概念了,乱码 也是一样的道理
哪些地方会出现编码问题呢?我们分几个场景来阐述
- 记事本(文本编辑器)
当我们将一堆字符敲完了,保存时文本编辑器一般都会有提示,你想以什么格式保存?
举个栗子会比较直观: 我们敲入了 '中文'
,保存时选择 UTF-8 编码,那么计算机内部就存储一个 '\xe4\xb8\xad\xe6\x96\x87'
,如果选择 GBK 编码,那么就存储一个 '\xd6\xd0\xce\xc4'
,这是编码的过程
相应的我们关闭编辑器后重新打开,可以选择以什么编码格式来打开,这就是解码的过程
所以如果编码方式和解码方式不一致,自然就无法将字节('\xe4\xb8\xad\xe6\x96\x87'
)转为 我们期望的字符('中文'
)
了解清楚了么,然后我们开始扩展到更多情景
ps: 不要用记事本写代码,容易编码出错是一方面,主要显得太 low 了
- 我们将字符串赋值给一个变量,然后输出到屏幕
这里除了要保证源代码文件保存时的编码和重新打开时的编码一致之外,多了一点需要注意
当程序运行起来的时候,程序内部也有自己的编码方式(wtf?),这里以 python2 为例
python2 内部默认格式是 unicode ,然而它也允许另外一种格式 str,em...
>>> s = '中文'
>>> s
'\xe4\xb8\xad\xe6\x96\x87'
>>> type(s)
<type 'str'>
可以看到这就是我们的字符串它的类型是 str,编码方式是 UTF-8
所以我们用 UTF-8 来解码一下,嗯,得到了一个 unicode 类型,真不错
>>> s.decode('utf-8')
u'\u4e2d\u6587'
>>> type(s.decode('utf-8'))
<type 'unicode'>
python 一定也是这么做的吧
no... 在 python2 中,默认使用 ascii 格式来处理的
也就是说,当 python2 检测到该字符串不是 unicode 类型,就尝试用 ascii 去解码,即 s.decode('ascii')
相应的,在输出的时候,检测到该字符串不是 str 类型,就尝试用 ascii 去编码
所以你就遇到了这样两个错误
UnicodeEncodeError: 'ascii' codec can't encode characters
UnicodeDecodeError: 'ascii' codec can't decode characters
对于这个编码转换发生在什么时候我不太清楚,知道的请告诉我!
不过这样下去总不是个事儿
最好的解决方法就是迁移到 python3 吧(强烈推荐)
python3 统一了字符串表示用 str,内部存储用的是 bytes,默认编码格式是 UTF-8!
更多信息自己去查询,这里有点跑偏了
- 我们浏览网页的时候
与上述情况类似,数据在网络上传播的形式是 比特流/字节流,这就意味着还是要编码
存储在服务器的网页信息(字符),在服务端编码变成比特流/字节流,然后我们的浏览器拿到这些比特流/字节流,解码成(我们实际看到的)网页(字符)
编码解码不一致就又会乱码啦!
不过一般网页都会指定编码方式 <meta charset="utf-8"/>
浏览器也会按指定格式解码,一般没事
出错了调整下浏览器编码方式试试
结语
差不多就这么多了,点个关注嘛..