1. 字符编码简介
阶段一:现代计算机起源于美国,最早诞生也是基于英文考虑的ASCII
ASCII:一个Bytes代表一个字符(英文字符/键盘上的所有其他字符),1Bytes=8bit,8bit可以表示0-2**8-1种变化,即可以表示256个字符
ASCII最初只用了后七位,127个数字,已经完全能够代表键盘上所有的字符了(英文字符/键盘的所有其他字符)
后来为了将拉丁文也编码进了ASCII表,将最高位也占用了
阶段二:为了满足中文,中国人定制了GBK
GBK:2Bytes代表一个字符
为了满足其他国家,各个国家纷纷定制了自己的编码
日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里
阶段三:各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。
于是产生了unicode, 统一用2Bytes代表一个字符, 2**16-1=65535,可代表6万多个字符,因而兼容万国语言
但对于通篇都是英文的文本来说,这种编码方式无疑是多了一倍的存储空间(二进制最终都是以电或者磁的方式存储到存储介质中的)
于是产生了UTF-8,对英文字符只用1Bytes表示,对中文字符用3Bytes
需要强调的一点是:
unicode:简单粗暴,所有字符都是2Bytes,优点是字符->数字的转换速度快,缺点是占用空间大
utf-8:精准,对不同的字符用不同的长度表示,优点是节省空间,缺点是:字符->数字的转换速度慢,因为每次都需要计算出字符需要多长的Bytes才能够准确表示
内存中使用的编码是unicode,用空间换时间(程序都需要加载到内存才能运行,因而内存应该是尽可能的保证快) 硬盘中或者网络传输用utf-8,网络I/O延迟或磁盘I/O延迟要远大与utf-8的转换延迟,而且I/O应该是尽可能地节省带宽,保证数据传输的稳定性。
2.字符编码的使用
2.1 文本编辑器一锅端
2.1.2 文本编辑器nodpad++
分析过程?什么是乱码
文件从内存刷到硬盘的操作简称存文件
文件从硬盘读到内存的操作简称读文件
乱码一:存文件时就已经乱码
存文件时,由于文件内有各个国家的文字,我们单以shiftjis去存,
本质上其他国家的文字由于在shiftjis中没有找到对应关系而导致存储失败,用open函数的write可以测试,f=open(‘a.txt‘,‘w‘,encodig=‘shift_jis‘)
f.write(‘你瞅啥\n何を見て\n‘) #‘你瞅啥‘因为在shiftjis中没有找到对应关系而无法保存成功,只存‘何を見て\n‘可以成功
但当我们用文件编辑器去存的时候,编辑器会帮我们做转换,保证中文也能用shiftjis存储(硬存,必然乱码),这就导致了,存文件阶段就已经发生乱码
此时当我们用shiftjis打开文件时,日文可以正常显示,而中文则乱码了
再或者,存文件时:
f=open(‘a.txt‘,‘wb‘) f.write(‘何を見て\n‘.encode(‘shift_jis‘)) f.write(‘你愁啥\n‘.encode(‘gbk‘)) f.write(‘你愁啥\n‘.encode(‘utf-8‘)) f.close()
以任何编码打开文件a.txt都会出现其余两个无法正常显示的问题
乱码二:存文件时不乱码而读文件时乱码
存文件时用utf-8编码,保证兼容万国,不会乱码,而读文件时选择了错误的解码方式,比如gbk,则在读阶段发生乱码,读阶段发生乱码是可以解决的,选对正确的解码方式就ok了,而存文件时乱码,则是一种数据的损坏。
2.1.3 文本编辑器pycharm
以gbk格式保存
以utf-8格式打开
分析过程?
总结:
无论是何种编辑器,要防止文件出现乱码(请一定注意,存放一段代码的文件也仅仅只是一个普通文件而已,此处指的是文件没有执行前,我们打开文件时出现的乱码)
核心法则就是,文件以什么编码保存的,就以什么编码方式打开
3. 程序的执行
python test.py (我再强调一遍,执行test.py的第一步,一定是先将文件内容读入到内存中)
阶段一:启动python解释器
阶段二:python解释器此时就是一个文本编辑器,负责打开文件test.py,即从硬盘中读取test.py的内容到内存中
此时,python解释器会读取test.py的第一行内容,#coding:utf-8,来决定以什么编码格式来读入内存,这一行就是来设定python解释器这个软件的编码使用的编码格式这个编码,
可以用sys.getdefaultencoding()查看,如果不在python文件指定头信息#-*-coding:utf-8-*-,那就使用默认的
python2中默认使用ascii,python3中默认使用utf-8
阶段三:读取已经加载到内存的代码(unicode编码的二进制),然后执行,执行过程中可能会开辟新的内存空间,比如x="egon"
内存的编码使用unicode,不代表内存中全都是unicode编码的二进制,
在程序执行之前,内存中确实都是unicode编码的二进制,比如从文件中读取了一行x="egon",其中的x,等号,引号,地位都一样,都是普通字符而已,都是以unicode编码的二进制形式存放与内存中的
但是程序在执行过程中,会申请内存(与程序代码所存在的内存是俩个空间),可以存放任意编码格式的数据,比如x="egon",会被python解释器识别为字符串,会申请内存空间来存放"hello",然后让x指向该内存地址,此时新申请的该内存地址保存也是unicode编码的egon,如果代码换成x="egon".encode(‘utf-8‘),那么新申请的内存空间里存放的就是utf-8编码的字符串egon了
针对python3如下图
浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器
如果服务端encode的编码格式是utf-8, 客户端内存中收到的也是utf-8编码的二进制。
4. python2与python3的区别
4.1 在python2中有两种字符串类型str和unicode
str类型
当python解释器执行到产生字符串的代码时(例如s=‘林‘),会申请新的内存地址,然后将‘林‘encode成文件开头指定的编码格式,这已经是encode之后的结果了,所以s只能decode
1 #_*_coding:gbk_*_ 2 #!/usr/bin/env python 3 4 x=‘林‘ 5 # print x.encode(‘gbk‘) #报错 6 print x.decode(‘gbk‘) #结果:林
所以很重要的一点是:
在python2中,str就是编码后的结果bytes,str=bytes,所以在python2中,unicode字符编码的结果是str/bytes
#coding:utf-8 s=‘林‘ #在执行时,‘林‘会被以conding:utf-8的形式保存到新的内存空间中 print repr(s) #‘\xe6\x9e\x97‘ 三个Bytes,证明确实是utf-8 print type(s) #<type ‘str‘> s.decode(‘utf-8‘) # s.encode(‘utf-8‘) #报错,s为编码后的结果bytes,所以只能decode
unicode类型
当python解释器执行到产生字符串的代码时(例如s=u‘林‘),会申请新的内存地址,然后将‘林‘以unicode的格式存放到新的内存空间中,所以s只能encode,不能decode
s=u‘林‘ print repr(s) #u‘\u6797‘ print type(s) #<type ‘unicode‘> # s.decode(‘utf-8‘) #报错,s为unicode,所以只能encode s.encode(‘utf-8‘)
打印到终端
对于print需要特别说明的是:
当程序执行时,比如
x=‘林‘
print(x) #这一步是将x指向的那块新的内存空间(非代码所在的内存空间)中的内存,打印到终端,而终端仍然是运行于内存中的,所以这打印可以理解为从内存打印到内存,即内存->内存,unicode->unicode
对于unicode格式的数据来说,无论怎么打印,都不会乱码
python3中的字符串与python2中的u‘字符串‘,都是unicode,所以无论如何打印都不会乱码
在pycharm中
在windows终端
但是在python2中存在另外一种非unicode的字符串,此时,print x,会按照终端的编码执行x.decode(‘终端编码‘),变成unicode后,再打印,此时终端编码若与文件开头指定的编码不一致,乱码就产生了
在pycharm中(终端编码为utf-8,文件编码为utf-8,不会乱码)
在windows终端(终端编码为gbk,文件编码为utf-8,乱码产生)
思考题:
分别验证在pycharm中和cmd中下述的打印结果
#coding:utf-8 s=u‘林‘ #当程序执行时,‘林‘会被以unicode形式保存新的内存空间中 #s指向的是unicode,因而可以编码成任意格式,都不会报encode错误 s1=s.encode(‘utf-8‘) s2=s.encode(‘gbk‘) print s1 #打印正常否? print s2 #打印正常否 print repr(s) #u‘\u6797‘ print repr(s1) #‘\xe6\x9e\x97‘ 编码一个汉字utf-8用3Bytes print repr(s2) #‘\xc1\xd6‘ 编码一个汉字gbk用2Bytes print type(s) #<type ‘unicode‘> print type(s1) #<type ‘str‘> print type(s2) #<type ‘str‘>
4.2 在python三种也有两种字符串类型str和bytes
str是unicode
#coding:utf-8 s=‘林‘ #当程序执行时,无需加u,‘林‘也会被以unicode形式保存新的内存空间中, #s可以直接encode成任意编码格式 s.encode(‘utf-8‘) s.encode(‘gbk‘) print(type(s)) #<class ‘str‘>
bytes是bytes
#coding:utf-8 s=‘林‘ #当程序执行时,无需加u,‘林‘也会被以unicode形式保存新的内存空间中, #s可以直接encode成任意编码格式 s1=s.encode(‘utf-8‘) s2=s.encode(‘gbk‘) print(s) #林 print(s1) #b‘\xe6\x9e\x97‘ 在python3中,是什么就打印什么 print(s2) #b‘\xc1\xd6‘ 同上 print(type(s)) #<class ‘str‘> print(type(s1)) #<class ‘bytes‘> print(type(s2)) #<class ‘bytes‘>
参考http://www.cnblogs.com/linhaifeng/articles/5950339.html