Table of Contents
先验知识
1 字符编码集介绍
二、字符在硬盘上的存储
GB2312 和GBK:
Unicode 统一码,
UTF-8编码
ANSI-外文编码
BOM
2 各类软件书写文本的编码方式
pycharm编码问题
Windows 自带的记事本
ultraedit
3 Python
编译器怎样读取字符的以什么编码格式存储在内存中的
查看Python编译器以什么编码方式读取文件代码 以及标准输出是按照什么编码格式
print 函数怎样处理字符串的
Python3执行过程
1 Python3获取bytes类型
文件在 Python2 和 Python3 环境下运行结果的区别,如下所示:
编码相互转换的规则如下:
五、Python bytes类型encode、decode
Python socket
2 chardet 是专门查看bytes类型,
2.1查看英文编码后的byes类型
2.2 查看汉语编码后的bytes类型
Python3文件编码和编译器读取代码文件分别是什么编码
下面这个讲解很好
https://www.jb51.net/article/133452.htm
windows默认的中文集就是GBK,两个字节表示。
Ubuntu 默认只是支持utf-8汉字编码,没有GBK
英文一个字节表示,一个字节是8位,
对于汉字『好』,用 str 表示时,它对应的 utf-8 编码 是'\xe5\xa5\xbd',对应的 gbk 编码是 '\xba\xc3',而用 unicode 表示时,他对应的符号就是u'\u597d',与u"好" 是等同的
Unicode 起到了2个作用:
直接支持全球所有语言,每个国家都可以不用再使用自己之前的旧编码了,用unicode就可以了。(就跟英语是全球统一语言一样)
unicode包含了跟全球所有国家编码的映射关系。 Unicode解决了字符和二进制的对应关系,但是使用unicode表示一个字符,太浪费空间。例如:利用unicode表示“Python”需要12个字节才能表示,比原来ASCII表示增加了1倍。 由于计算机的内存比较大,并且字符串在内容中表示时也不会特别大,所以内容可以使用unicode来处理,但是存储和网络传输时一般数据都会非常多,那么增加1倍将是无法容忍的!!!
为了解决存储和网络传输的问题,出现了Unicode Transformation Format,学术名UTF,即:对unicode中的进行转换,以便于在存储和网络传输时可以节省空间!
UTF-8: 使用1、2、3、4个字节表示所有字符;优先使用1个字符、无法满足则使增加一个字节,最多4个字节。英文占1个字节、欧洲语系占2个、东亚语系占3个,其它及特殊字符占4个。
UTF-16: 使用2、4个字节表示所有字符;优先使用2个字节,否则使用4个字节表示。
UTF-32: 使用4个字节表示所有字符。
总结:UTF 是为unicode编码 设计 的一种在存储和传输时节省空间的编码方案。
首先要明确的一点就是,无论以什么编码在内存里显示字符,存到硬盘上都是2进制(0b是说明这段数字是二进制,0x表示是16进制。0x几乎所有的编译器都支持,而支持0b的并不多)。理解这一点很重要。
还要注意的一点是:
存到硬盘上时是以何种编码存的,再从硬盘上读出来时,就必须以何种编码读(开头声明或转换),要不然就乱了。
虽然有了unicode and utf-8 ,但是由于历史问题,各个国家依然在大量使用自己的编码,比如中国的windows,默认编码依然是gbk,而不是utf-8。基于此,如果中国的软件出口到美国,在美国人的电脑上就会显示乱码,因为他们没有gbk编码。所以该怎么办呢?
还记得我们讲unicode其中一个功能是其包含了跟全球所有国家编码的映射关系,这时就派上用场了。无论你以什么编码存储的数据,只要你的软件在把数据从硬盘读到内存里,转成unicode来显示,就可以了。由于所有的系统、编程语言都默认支持unicode,那你的gbk软件放到美国电脑上,加载到内存里,变成了unicode,中文就可以正常展示啦。
汉字:2个字节,英文字母或半角标点: 1个字节
用0-0x10FFFF来映射全球各国的语言文字,Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符。
第一种方案:UTF-32编码每个字符用一个int来表示。特点:简单,但太浪费空间abcd -> 16个字节
第二种方案:UTF-16编码。特点: 用1~2个short来表示一个字符
第三种方案:UTF-8编码。用1~4个字节来表示一个字符(比较节省空间)
如果纯写英文,那么就用1个字节,如果是汉字,那么就不好说了,可能是2或者3个字节,在ultraedit上写上几个字符abc1234,在转换为utf-16编码,即使是英文也要用两个字节进行编码。
输入邵发abc1234,发现 邵 字用3个字节表示,发 字也用3个字节表示
解释一下,为什么后面是乱码,因为windows默认字符集是GBK,windows是以GBK的编码格式显示utf-8编码格式的字符集,因此会是乱码,切换到原文,为什么可以正常显示,是因为,我将GBK字符转换成了utf-8编码格式,这个文本软件可以自动转换,转换后是以utf-8的格式进行显示,所以不是乱码,因此每个软件默认有自己的显示编码的格式,比如pycharm的 python2 必须在第一行加入utf-8,为什么?
因为不是文本的问题,文本依然是使用GBK的编码
ANSI编码一般指Windows-1252编码,是一个256个字符的字集的编码,每个字符由一个字节表示。其中前128个字符(00-7F)和ASCII的7bits编码一样,后128个字符中有一些欧洲国家用的有重音的字符。ANSI编码在不同语言的Windows下也指此语言下的Windows编码页,比如中文环境下指Windows-936(也就是GB2312),日文环境下是Windows-932(JIS)编码等等,也是前128个字符(00-7F)和ASCII的7bits编码一样,其他字符则由2个字节表示。
UTF-8是针对Unicode的可变长度字符编码,一个字符可以由1到4个字节表示,其中由一个字节表示的字符和ASCII的7bits编码一样,而包括中文在内的大部分字符则由3个字节表示。
所以如果文本里只有ASCII的7bits编码的那些,这两种编码是互相兼容没有区别的,但是对其他字符,编码就不同了,而且Windows-1252编码无法表达除了256个字符外的比如中文字符,其他的ANSI编码如Windows-936也只能表示一部分Unicode中的字符。编码格式的不同导致程序无法运行很容易理解,因为同样的字集在不同的编码方式下表达的字符是不同的或者是不能被表示的,除非是ASCII的7bits编码中的那些字符。
问题:假设一个软件比如pycharm用utf-8写了一段字符,但是用windows自带的记事本(默认GBK编码)打开并不是乱码,另存为文件时候,默认显示竟然是utf-8,我就是想知道记事本凭什么知道我pycharm用utf-8编码方式写的代码,记事本是怎样识别出来的。答案就是BOM,前3个字节,就是告诉软件这个文本文件是utf-8编码的,使用十六进制查看就知道了
Windows就是使用BOM来标记文本文件的编码方式的。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。
使用UTF-8编码保存,这种称呼也是不正确的,正常UTF-8编码的二进制是没有BOM标识的,而windows上的UTF-8编码的文件时有UTF-8 BOM标识(EF BB BF)
UTF-8编码的文件中,BOM占三个字节。如果用记事本把一个文本文件另存为UTF-8编码方式的话,用UE打开这个文件,切换到十六进制编辑状态就可以看到开头的FFFE了。这是个标识UTF-8编码文件的好办法,软件通过BOM来识别这个文件是否是UTF-8编码,很多软件还要求读入的文件必须带BOM。可是,还是有很多软件不能识别BOM。我在研究Firefox的时候就知道,在Firefox早期的版本里,扩展是不能有BOM的,不过Firefox 1.5以后的版本已经开始支持BOM了。现在又发现,PHP也不支持BOM。
下面就是abc1234 的utf-16的编码方式,ff fE 就是标识符表示utf-16的编码方式
很多软件Unicode 编码默认就是utf-16编码,都是别称,因此要注意,但是大部分软件的默认编码方式是utf-8的方式
pycharm 第一行写入 # coding:utf-8 结果pycharm就将该代码文本强制转换成utf-8编码格式,而且pycharm不能更改
如图
Windows下记事本保存文本文件的时候,可以选择不同的编码格式来保存文件,各种编码保存的文件的二进制是不同的,举例说明:
我们在记事本中输入123,选择默认的编码格式,即ANSI,也就是系统默认的编码格式,简体中文版的默认编码格式为GBK,此时我们使用二进制工具打开时,其二进制形式为:
31 32 33
使用Unicode编码保存,实际上,这种称呼是不正确的,Unicode只是表示字符集方案,并不能表示编码方案,windows对Unicode实际上采用的编码方案是UTF-16LE,其会在文本的开头插入小段字节序标识BOM(FFFE),故其二进制为:
FF FE 31 00 32 00 33 00
使用Unicode big endian编码保存,这种称呼也是不正确的,windows实际上采用的编码方案是UTF-16BE,其会在文本的开头插入大端字节序标识BOM(FEFF),故其二进制为:
FE FF 00 31 00 32 00 33
使用UTF-8编码保存,这种称呼也是不正确的,正常UTF-8编码的二进制是没有BOM标识的,而windows上的UTF-8编码的文件是有UTF-8 BOM标识(EF BB BF),故其二进制为:
EF BB BF 31 32 33
举例:
在windows下新建文本文档,里面写入 a我 字样,保存为ANSI格式就是GBK格式,将此文件导入pycharm中显示,结果
pycharm是以utf-8格式进行显示的,因此就是乱码
在ultraedit软件中输入邵发 abc1234 ,用16进制查看
邵 字对应着 C9 DB,两个字节, a 对应着 61 一个字节,注意61不是 十进制的六十一,而是二进制(0110 0001 )=十进制
64+32+1=97 完美 小写字母a就是数字97.
u 这个意思是表示好是安装Unicode编码格式,这个在Python3里面没什么用,因为解释器读取好的时候就是按照Unicode编码格式存入内存中的,对于Python2 就是管用的了
a = u"好"
下面这个代码是查看Python编译器以什么编码方式读取文件代码的,Python2当出现汉字的时候就会报错,因为默认解释器按照asiii码读取utf-8代码,自然就会出现语法错误了,但是Python里面是按照utf-8读取代码,自然不会报错
import sys
print sys.getdefaultencoding() # 编译器自己按照什么编码方式读取代码,Python2默认是ascii码
print sys.stdout.encoding # 查看print 函数是以什么编码方式进行输出的,这就是标准输出,一般是utf-8
python3 python2
简单说就是命令行出来的基本是什么就显示什么,而print则是尽量显示人类可以看懂的东西
比如Python2里面 a是utf-8编码,a直接输出他的Unicode码,因为Unicode码是存在计算机上面的本质-这里还是有点不懂,Python3 就不一样
Python2.7中调用print打印var 变量时,操作系统会对var做一定的字符处理:如果var是str类型的变量,则直接将var变量交付给终端进行显示;如果var变量是unicode类型,则操作系统首先将var编码成str类型的对象(编码格式取决于stdout的编码格式),然后再交由终端进行显示。在终端显示时,如果str类型的变量的编码方式和终端设置的编码方式不一致,很可能会出现乱码问题。
问题:Python2里为什么直接输出a打印乱码,而print a就是字符呢?
答案:在Python2里 对于print 函数打印:
a在内存中以utf-8编码的形式在内存中,a 是一个str类型,print 以什么样式的编码方式输出,取决于 下面这个代码的输出
如图知 print 是以utf-8的编码方式输出,因此正常显示。
print 打印做了几件事情,1,a是一个utf-8的编码格式存入内存了,然后把a转换成utf-8(等于没转),在以utf-8格式进行显示
Python3 print是 a是一个Unicode的编码格式存入内存了,print 把a转换成utf-8,在以utf-8格式进行显示
Python3执行过程
在py3上 把你的代码以utf-8编写, 保存,然后在windows上执行。发现可以正常执行!其实utf-8编码之所以能在windows gbk的终端下显示正常,是因为到了内存里python解释器把utf-8转成了unicode , 但是这只是python3, 并不是所有的编程语言在内存里默认编码都是unicode,比如 万恶的python2 就不是,它是ASCII(龟叔当初设计Python时的一点缺陷),想写中文,就必须声明文件头的coding为gbk or utf-8, 声明之后,python2解释器仅以文件头声明的编码去解释你的代码,加载到内存后,并不会主动帮你转为unicode,也就是说,你的文件编码是utf-8,加载到内存里,你的变量字符串就也是utf-8, 这意味着什么?意味着,你以utf-8编码的文件,在windows是乱码。其实乱是正常的,不乱才不正常,因为只有2种情况 ,你的windows上显示才不会乱。 Python2并不会自动的把文件编码转为unicode存在内存里。
1、字符串以GBK格式显示
2、字符串是unicode编码
所以我们只有手动转,Python3 自动把文件编码转为unicode必定是调用了什么方法,这个方法就是,decode(解码) 和encode(编码)。
UTF-8/GBK --> decode 解码 --> Unicode
Unicode --> encode 编码 --> GBK / UTF-8
Python3
获取bytes类型英文变成bytes类型方法还可以有有 b'abc', 这种方法仅适用于英文
bytes 转成字符 就必须知道 这个bytes是按照什么编码形式编码,否则就会报错,
最后总结一下:
python3 文件默认编码是utf-8 , 字符串编码是 unicode
以utf-8 或者 gbk等编码的代码,加载到内存,会自动转为unicode正常显示。
python2 文件默认编码是ascii , 字符串编码也是 ascii , 如果文件头声明了是gbk,那字符串编码就是gbk。
以utf-8 或者 gbk等编码的代码,加载到内存,并不会转为unicode,编码仍然是utf-8或者gbk等编码。由于不会转换成Unicode编码方式,于是就设置了一个新的数据类型就叫Unicode数据类型,注意这个Unicode不是编码方式而是数据类型。这个数据类型就是按照Unicode编码方式编写的,只能手动设置。
五、Python bytes类型
encode、decodePython3 的bytes 不能打印为什么?
因为bytes不是字符了,而是字节,bytes在Python3的本质就是16进制数字而已。所以打印不出来,这是与Python2明显的区别
Python3 里面的encode 和decode 分别是从字符类型转换成字节类型和字节类型转换成字符类型。不光光是转换字符编码还是变化数据类型。
把8个二进制一组称为一个byte,用16进制来表示。为的就是让人们看起来更可读。我们称之为bytes类型,即字节类型。
python2的字符串其实更应该称为字节串。 通过存储方式就能看出来, 但python2里还有一个类型是bytes呀,难道又叫bytes又叫字符串?
嗯 ,是的,在python2里,bytes == str , 其实就是一回事。
除此之外呢, python2里还有个单独的类型是unicode , 把字符串解码后,就会变成unicode。
Python2的默认编码是ASCII码,当后来大家对支持汉字、日文、法语等语言的呼声越来越高时,Python于是准备引入unicode,但若直接把
默认编码改成unicode的话是不现实的,因为很多软件就是基于之前的默认编码ASCII开发的,编码一换,那些软件的编码就都乱了。所以Python 2就直接搞了一个新的字符类型,就叫unicode类型--注意这就是一个数据类型和class一样,比如你想让你的中文在全球所有电脑上正常显示,在内存里就得把字符串存成unicode类型。
注意:
Python3 除了把字符串的编码改成了unicode, 还把str 和bytes 做了明确区分, str 就是unicode格式的字符, bytes就是单纯二进制啦。
在py3里看字符,必须得是unicode编码,其它编码一律按bytes格式展示。
Python只要出现各种编码问题,无非是哪里的编码设置出错了
常见编码错误的原因有以下这些:
Python解释器的默认编码
Python源文件文件编码
Terminal使用的编码
操作系统的语言设置
import chardet
import sys
print(sys.getdefaultencoding()) # utf-8
print(sys.stdout.encoding) # 打印stdout的编码方式 UTF-8
string = b'abc'.decode() # string是字符类型
b = string.encode('utf-8') # b是bytes类型
print(chardet.detect(b)) # b必须是bytes类型,否则报错
# 为什么是ascii码编码,因为英文就是ascii码,没有其他的码
# {'encoding': 'ascii', 'confidence': 1.0, 'language': ''}
说明 纯英文的bytes类型只可能是ascii编码方式,其他的指定是啥就是啥。
'''
'测试'.encode('gbk')
意思是:“测试” 这个字符串 它本身作为gbk的编码方式,进行编码,编码出来一个bytes类型
"测试".encode() # 默认utf-8
意思是:“测试” 这个字符串 它本身作为utf-8的编码方式,进行编码,编码出来一个bytes类型
'''
import chardet
import sys
a = "测试".encode() # 默认utf-8
print(chardet.detect(a))
# {'encoding': 'utf-8', 'confidence': 0.7525, 'language': ''}
import chardet
data = '离离原上草,一岁一枯荣'.encode('gbk')
print(chardet.detect(data))
# {'encoding': 'GB2312', 'confidence': 0.7407407407407407, 'language': 'Chinese'}