关于python中的字符编码

一 了解字符编码的知识储备

  1. 文本编辑器存取文件的原理(nodepad++,pycharm,word)node

    打开编辑器就打开了启动了一个进程,是在内存中的,因此在编辑器编写的内容也都是存放与内存中的,断电后数据丢失python

             于是须要保存到硬盘上,点击保存按钮,就从内存中把数据刷到了硬盘上。编程

             在这一点上,咱们编写一个py文件(没有执行),跟编写其余文件没有任何区别,都只是在编写一堆字符而已。windows

      2. python解释器执行py文件的原理 ,例如python test.py浏览器

    第一阶段:python解释器启动,此时就至关于启动了一个文本编辑器服务器

    第二阶段:python解释器至关于文本编辑器,去打开test.py文件,从硬盘上将test.py的文件内容读入到内存中网络

    第三阶段:python解释器解释执行刚刚加载到内存中test.py的代码  编辑器

  总结:this

  1. python解释器是解释执行文件内容的,于是python解释器具有读py文件的功能,这一点与文本编辑器同样
  2. 与文本编辑器不同的地方在于,python解释器不只能够读文件内容,还能够执行文件内容

二 什么是字符编码

  计算机要想工做必须通电,也就是说‘电’驱使计算机干活,而‘电’的特性,就是高低电平(高低平即二进制数1,低电平即二进制数0),也就是说计算机只认识数字 编码

  编程的目的是让计算机干活,而编程的结果说白了只是一堆字符,也就是说咱们编程最终要实现的是:一堆字符驱动计算机干活

  因此必须通过一个过程:

  字符--------(翻译过程)------->数字 

  这个过程实际就是一个字符如何对应一个特定数字的标准,这个标准称之为字符编码

三 字符编码的发展史

阶段一:现代计算机起源于美国,最先诞生也是基于英文考虑的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才可以准确表示

  1. 内存中使用的编码是unicode,用空间换时间(程序都须要加载到内存才能运行,于是内存应该是尽量的保证快)
  2. 硬盘中或者网络传输用utf-8,网络I/O延迟或磁盘I/O延迟要远大与utf-8的转换延迟,并且I/O应该是尽量地节省带宽,保证数据传输的稳定性。

 

四.字符编码分类

计算机由美国人发明,最先的字符编码为ASCII,只规定了英文字母数字和一些特殊字符与数字的对应关系。

ascii用1个字节(8位二进制)表明一个字符

unicode经常使用2个字节(16位二进制)表明一个字符,生僻字须要用4个字节

若是咱们的文档通篇都是英文,你用unicode会比ascii耗费多一倍的空间,在存储和传输上十分的低效

本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不一样的数字大小编码成1-6个字节,经常使用的英文字母被编码成1个字节,汉字一般是3个字节,只有很生僻的字符才会被编码成4-6个字节。若是你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:

字符 ASCII Unicode UTF-8
A 01000001 00000000 01000001 01000001
x 01001110 00101101 11100100 10111000 10101101

从上面的表格还能够发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上能够被当作是UTF-8编码的一部分,因此,大量只支持ASCII编码的历史遗留软件能够在UTF-8编码下继续工做。

五 字符编码的使用

5.1 文本编辑器一锅端

 

5.1.2 文本编辑器nodpad++

总结:

不管是何种编辑器,要防止文件出现乱码(请必定注意,存放一段代码的文件也仅仅只是一个普通文件而已,此处指的是文件没有执行前,咱们打开文件时出现的乱码)

核心法则就是,文件以什么编码保存的,就以什么编码方式打开

而文件编码保存时候使用的编码方式是右下角的编码方式,而解码的时候是使用文档开头申明的编码方式,两种编码不一样的时候很容易出现乱码的状况。

 

5.2 程序的执行

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编码的二进制。

 

5.3 python2与python3的区别

 

5.3.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'>
复制代码

 

5.3.2 在python3中也有两种字符串类型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'>
复制代码
相关文章
相关标签/搜索