一篇文章详解python的字符编码问题
一:什么是编码
将明文转换为计算机可以识别的编码文本称为“编码”。反之从计算机可识别的编码文本转回为明文为“解码”。
那么什么是明文呢,首先我们从一段信息说起,消息以人们可以理解,易懂的表示存在,我们把这个表示为明文(plain text)。对于说英文的人,纸张上打印的或者屏幕上显示的英文都算是明文。
二:都有什么编码格式?
1:ASCII(占一个字符,只支持英文)
计算机上的数据都是以二进制的形式存储的,1个字节(8比特)可以表示256种状态,英文只有26个字符,再加上一些特殊的字符,使用128个就够了,计算机就可以使用127个不同字节来表示英文文字,这就是ASCII码
2:GB2312(占两个字符,支持6700+汉字)
计算机进入中国后,无法显示中文,一个字节已经被占满了,我国重新制定了一个编码表,将扩展的第八位对应的拉丁文全部删掉,规定一个小于127的字符与原来的意义相同,当两个大于127的字符连接在一起的时候,就表示一个汉字,前面一个字节为高字节,后面一个字节为低字节,这样就可以表示7000多汉字,这种编码叫做GB2312。GB2312是对ASCII的中文扩展
3:GBK和GB18030(GB2312的升级版,支持21000+汉字)
由于汉字的数量太大,GB2312是不能满足需求,后面规定只要第一个字节大于127就固定表示一个汉字,不管后面的是不是扩展字符里面的内容,扩展后的编码称为GBK,GBK包括了GB2312的所有内容,同时增加了近20000个新的汉字和符号
4:Shift-JIS 日本编码(这里不过多解释)
5:TIS-620泰国编码(这里不过多解释)
6:ks_c_5601-1987韩国编码(这里不过多解释)
由于每个国家都有自己的字符,所以其对应关系也涵盖了自己国家的字符,但是以上编码都存在局限性,即:仅涵盖本国字符,无其他国家字符的对应关系。应运而生出现了万国码,他涵盖了全球所有的文字和二进制的对应关系
7:Unicode(2-4字节,已经收录了136690个字符,并一直扩展)
在uincode出现之前,每隔国家都搞自己的编码,彼此之间互不支持,带来了许多不方便,国际标准组织提出来一个统一的编码标准:unicode
unicode用两个字符来表示一个字符,可以提供65535种字符,足够覆盖世界上所有的字符
8:UTF-8(Unicode Transformation Format)
unicode的出现,提供了统一的标准,但对于英文世界来说,一个字节完全够用,如果使用unicode会浪费大量的空间,为了解决这个问题提出来utf-8,一种针对unicode的可变长度字符串,可以使用1-4个字符表示一个符号,根据不同的符号变化字节长度,当字符在ASCII编码范围内,用一个字节表示,兼用ASCII。
使用这样编码的好处是,虽然内存汇总的数据都是unicode,但是当数据保存到磁盘或者用于网络传输时,使用utf-8会节省更多的流量和硬盘空间。
- UTF-8: 使用1、2、3、4个字节表示所有字符;优先使用1个字符、无法满足则使增加一个字节,最多4个字节。英文占1个字节、欧洲语系占2个、东亚占3个,其它及特殊字符占4个
- UTF-16: 使用2、4个字节表示所有字符;优先使用2个字节,否则使用4个字节表示。
- UTF-32: 使用4个字节表示所有字符;
unicode和utf-8的关系:unicode是内存编码表示方案,而utf-8是如何保存和传输unicode的方案
三:编码的转换
虽然国际语言是英语 ,但大家在自己的国家依然说自已的语言,不过出了国, 你就得会英语
编码也一样,虽然有了unicode and utf-8 , 但是由于历史问题,各个国家依然在大量使用自己的编码,比如中国的windows,默认编码依然是gbk,而不是utf-8
基于此,如果中国的软件出口到美国,在美国人的电脑上就会显示乱码,因为他们没有gbk编码。
若想让中国的软件可以正常的在 美国人的电脑上显示,只有以下2条路可走:
- 让美国人的电脑上都装上gbk编码
- 把你的软件编码以utf-8编码
第1种方法几乎不可能实现,第2种方法比较简单。 但是也只能是针对新开发的软件。 如果你之前开发的软件就是以gbk编码的,上百万行代码可能已经写出去了,重新编码成utf-8格式也会费很大力气。
so , 针对已经用gbk开发完毕的项目,以上2种方案都不能轻松的让项目在美国人电脑上正常显示,难道没有别的办法了么?
有, 还记得我们、、讲unicode其中一个功能是其包含了跟全球所有国家编码的映射关系,意思就是,你写的是gbk的“学习”,但是unicode能自动知道它在unicode中的“学习”的编码是什么,如果这样的话,那是不是意味着,无论你以什么编码存储的数据 ,只要你的软件在把数据从硬盘读到内存里,转成unicode来显示,就可以了。
由于所有的系统、编程语言都默认支持unicode,那你的gbk软件放到美国电脑 上,加载到内存里,变成了unicode,中文就可以正常展示啦。
unicode与gbk的映射表 http://www.unicode.org/charts/
四:python2.x的编码
在python2.x中,有两种字符串类型:str类型和unicode类型。这两个类型只是python定义的两个名字,关键还要看这两种数据类型在内存中的存储方法是什么
str和unicode都是basestring的子类。严格意义上说,str其实是字节串,它是unicode经过编码后的字节组成的序列。
而unicode是一个字符串,str是unicode这个字符串经过编码(utf8,gbk等)后的字节组成的序列。
unicode才是真正意义上的字符串,对字节串str使用正确的字符编码进行解码后获得
在Py2里,str=bytes。python2的字符串其实更应该称为字节串
py2编码的最大特点是Python 2 将会自动的将bytes数据解码成 unicode 字符串
所以在2里我们可以将字节与字符串拼接。
Python 2.7. (v2.7.14:84471935ed, Sep , ::) [MSC v. bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> s = '学习'
>>> print type(s)
<type 'str'>
>>> print repr(s)
'\xd1\xa7\xcf\xb0' >>> s = u'学习'
>>> print type(s)
<type 'unicode'>
>>> print repr(s)
u'\u5b66\u4e60'
由上面的例子可以看出str和unicode分别存储的是字节数据和unicode数据
但是,python2.x悄悄掩盖掉了byte到unicode的转换,只要数据全部是ASCII的话,所有的转换都是正确的,一个一个非ASCII的字符进入你的程序,那么默认的解码将会失效,从而造成unicodedecodeerror的错误,py2编码让程序在出路ASCII的时候非常简单,付出的代价就是在处理非ASCII的时候将会失败。
由于Python创始人在开发初期认知的局限性,其并未预料到python能发展成一个全球流行的语言,导致其开发初期并没有把支持全球各国语言当做重要的事情来做,所以就轻佻的把ASCII当做了默认编码。 当后来大家对支持汉字、日文、法语等语言的呼声越来越高时,Python于是准备引入unicode,但若直接把默认编码改成unicode的话是不现实的, 因为很多软件就是基于之前的默认编码ASCII开发的,编码一换,那些软件的编码就都乱了。所以Python 2 就直接 搞了一个新的字符类型,就叫unicode类型,比如你想让你的中文在全球所有电脑上正常显示,在内存里就得把字符串存成unicode类型
五:python3.x的编码
python3.x也有两种数据类型,str和bytes;str类型存unicode数据,bytes类型存bytes数据,
python3.x将utf-8或者gbk等编码的字节数据转化为python3.x的str类型,utf-8编码的bytes<-->str
python2.x将utf-8或者gbk等编码的字节数据转化为python2.x的unicode类型,utf-8编码的str<-->unicode
python3.x的编码思想就是它清晰的将文本和二进制区分开了,不会对bytes字节进行自动编码。文本总是unicode,由str类型表示,二进制数据则由bytes类型表示,Python 3不会以任意隐式的方式混用str和bytes,将两者明确地区分开。基于此,Python3中不能拼接字符串和字节包,也不可以在字节包里搜索字符串(反之亦然),也不能向使用字符串参数的函数中传入字节包参数(反之亦然)。
python3 执行代码的过程
- 解释器找到代码文件,把代码字符串按文件头定义的编码加载到内存,转成unicode
- 把代码字符串按照语法规则进行解释,
- 所有的变量字符都会以unicode编码声明
######在python2.x#######
print(b'hello'+'world')
会输出
helloworld ######在python3.x#######
print(b'hello'+'world')
会输出
TypeError: can't concat str to bytes
本来这样就可以结束了,但是上面的utf-8编码之所以能在windows gbk的终端下显示正常,是因为到了内存里python解释器把utf-8转成了unicode , 但是这只是python3, 并不是所有的编程语言在内存里默认编码都是unicode,比如 万恶的python2 就不是, 它的默认编码是ASCII,想写中文,就必须声明文件头的coding为gbk or utf-8, 声明之后,python2解释器仅以文件头声明的编码去解释你的代码,加载到内存后,并不会主动帮你转为unicode,也就是说,你的文件编码是utf-8,加载到内存里,你的变量字符串就也是utf-8, 这意味着什么你知道么?。。。意味着,你以utf-8编码的文件,在windows是乱码。
乱是正常的,不乱才不正常,因为只有2种情况 ,你的windows上显示才不会乱
- 字符串以GBK格式显示
- 字符串是unicode编码
既然Python2并不会自动的把文件编码转为unicode存在内存里, 那就只能使出最后一招了,你自己人肉转。Py3 自动把文件编码转为unicode必定是调用了什么方法,这个方法就是,decode(解码) 和encode(编码)
时间来到2008年,python发展已近20年,创始人龟叔越来越觉得python里的好多东西已发展的不像他的初衷那样,开始变得臃肿、不简洁、且有些设计让人摸不到头脑,比如unicode 与str类型,str 与bytes类型的关系,这给很多python程序员造成了困扰。
龟叔再也忍不了,像之前一样的修修补补已不能让Python变的更好,于是来了个大变革,Python3横空出世,不兼容python2,python3比python2做了非常多的改进,其中一个就是终于把字符串变成了unicode,文件默认编码变成了utf-8,这意味着,只要用python3,无论你的程序是以哪种编码开发的,都可以在全球各国电脑上正常显示,真是太棒啦!
PY3 除了把字符串的编码改成了unicode, 还把str 和bytes 做了明确区分, str 就是unicode格式的字符, bytes就是单纯二进制啦。
六:文件存储读取过程中的编码问题
对于文本编辑器word等软件,当我们在这些软件上编辑文字的时候,无论是什么语言的文字或符号,计算机都是无法识别的。
那么在保存之前数据是通过什么形式存在内存的呢?
是unicode数据,为什么要存unicode数据,这是因为无论世界上的任何字符它都有唯一编码对应,兼容性是最好的。
当我们保存了存到磁盘上的数据又是什么呢?
是通过某种编码方式编码的bytes字节串。比如utf8---一种可变长编码,很好的节省了空间;还可以是gbk等编码方式。
在我们的文本编辑器软件都有默认的保存文件的编码方式,比如utf-8,gbk等。当我们保存的时候,这些编辑软件已经"默默地"做了编码工作。
那当我们再打开这个文件时,软件又默默地给我们做了解码的工作,将数据再解码成unicode,然后就可以呈现明文给用户了!
所以,unicode是离用户更近的数据,bytes是离计算机更近的数据。
七:编码与程序运行的关系
编写python代码一般会用到pycharm ,sublime等软件,而代码文件的创建,保存,执行等过程就伴随着编码解码流程,使用pycharm创建的hello.py文件,当我们保存的时候,文件就以pycharm默认的编码方式保存到磁盘,关闭文件再打开,pycharm就会以默认的编码方式对该文件打开后读到的内容就行解码,转成unicode到内存我们就看到了我们的明文;
而如果点开运行按钮或者在命令行运行该文件时候,python解释器就会被调用,打开文件,然后将存储在磁盘上的bytes数据解码成unicode数据,这个过程和编译器是一样的,不同的是解释器将会把这些unicode数据翻译成c代码再转成二进制的数据流,最后通过控制操作系统调用cpu来执行这些二进制数据,整个过程才算结束,
python2.x默认的是ASCII码,python3.x默认的是utf-8,可以通过下面的方式查询:
import sys
print(sys.getdefaultencoding())
八,字符编码转换总结
python2.x
内存中字符默认编码是ASCII,默认文件编码也是ASCII
当声明了文件头的编码后,字符串的编码就按照文件编码来,总之,文件编码是什么,那么python2.x的str就是什么
python2.x的unicode是一个单独的类型,按u"编码"来表示
python2.x str==bytes,bytes直接是按照字符编码存成2进制格式在内存里
python3.x
字符串都是unicode
文件编码都默认是utf-8,读到内存会被python解释器自动转成unicode
bytes和str做了明确的区分
所有的unicode字符编码后都会编程bytes格式
九:print语句和print函数的区别
print语句
在python2.x中,print语句最简单的使用形式是
print hello world!
这相当于执行了
sys.stdout.write(‘’。join(map(str,[hello world!]))+'\n')
如果以逗号为分隔符,传递额外的参数,这些参数会被传递到str()函数,最终打印的时候,每个参数之间会空一行。
从2.0版本开始,python引入了print>>的语法。作用是重定向print语句最终输出的字符串的文件
例如:
print>>output 相当于 output.write(str(hello)+'\n')
print函数
如果用python来实现print的函数,他的函数定义应该是这样的
import sys def print(*objects, sep=None, end=None, file=None, flush=False):
"""A Python translation of the C code for builtins.print(). """
if sep is None:
sep = ' '
if end is None:
end = '\n'
if file is None:
file = sys.stdout
file.write(sep.join(map(str, objects)) + end)
if flush:
file.flush() 函数定义
从上面的代码我们可以发现,python3.x的print 函数实现了print语句的所有特性。
一篇文章详解python的字符编码问题的更多相关文章
- 详解python的字符编码问题
一:什么是编码 将明文转换为计算机可以识别的编码文本称为“编码”.反之从计算机可识别的编码文本转回为明文为“解码”. 那么什么是明文呢,首先我们从一段信息说起,消息以人们可以理解,易懂的表示存在,我们 ...
- 一篇文章详解iOS之AutoResizing、AutoLayout、sizeClass来龙去脉
前言 iPhone自诞生以来,随着其屏幕尺寸不断的多样化,屏幕适配的技术一直在发展更新.目前,iOS系统版本已经更新到9.3,XCode的最新版本已经是7.3,仅iPhone历史产品的尺寸就已经有4种 ...
- Python常见字符编码间的转换
主要内容: 1.Unicode 和 UTF-8的爱恨纠葛 2.字符在硬盘上的存储 3.编码的转换 4.验证编码是否转换正确 5.Python bytes类型 前 ...
- 详解Python中re.sub--转载
[背景] Python中的正则表达式方面的功能,很强大. 其中就包括re.sub,实现正则的替换. 功能很强大,所以导致用法稍微有点复杂. 所以当遇到稍微复杂的用法时候,就容易犯错. 所以此处,总结一 ...
- 详解Python中内置的NotImplemented类型的用法
它是什么? ? 1 2 >>> type(NotImplemented) <type 'NotImplementedType'> NotImplemented 是Pyth ...
- Python基础-字符编码与转码
***了解计算机的底层原理*** Python全栈开发之Python基础-字符编码与转码 需知: 1.在python2默认编码是ASCII, python3里默认是utf-8 2.unicode 分为 ...
- 举例详解Python中的split()函数的使用方法
这篇文章主要介绍了举例详解Python中的split()函数的使用方法,split()函数的使用是Python学习当中的基础知识,通常用于将字符串切片并转换为列表,需要的朋友可以参考下 函数:sp ...
- Python的字符编码
Python的字符编码 1. Python字符编码简介 1. 1 ASCII Python解释器在加载.py文件的代码时,会对内容进行编码,一般默认为ASCII码.ASCII(American St ...
- Python常用字符编码(转)
Python常用字符编码 字符编码的常用种类介绍 第一种:ASCII码 ASCII(American Standard Code for Information Interchange,美国信息交 ...
随机推荐
- 安装 redis 拓展
PHP API 20121113 PHP Extension 20121212 Zend Extension 220121212 Zend Extension Build API220121212,T ...
- Wpf窗口中打开WinForm窗口
获取wpf窗口对应的句柄窗口 using System; using System.Windows; using System.Windows.Interop; using IWin32Window ...
- 信号量(Semaphore)
在python的多线程体系中,一共有4种锁: 同步锁(互斥锁):Lock: 递归锁:RLock: 信号量:Semaphore: 同步条件锁:Condition. 信号量(semaphore)是一种可以 ...
- Windows下安装BeautifulSoup4显示'You are trying to run the Python 2 version of Beautiful Soup under Python 3.(`python setup.py install`) or by running 2to3 (`2to3 -w bs4`).'
按照网上教程,将cmd的目录定位到解压缩文件夹地址,然后 >>python setup.py install ( Window下不能直接解压tar.giz文件,可以使用7z解压软件提取解压 ...
- 创建JUtil
这里拿Dynamic项目来演示,首先创建一个Dynamic项目,起名,点next, 继续点next, 将web.xml文件勾选,finish, 接下来在Java Resources->src下创 ...
- 内存栅栏(memory barrier):解救peterson算法的应用陷阱
最近一个项目中用到了peterson算法来做临界区的保护,简简单单的十几行代码,就能实现两个线程对临界区的无锁访问,确实很精炼.但是在这不是来分析peterson算法的,在实际应用中发现peterso ...
- 用不到 50 行的 Python 代码构建最小的区块链
引用 译者注:随着比特币的不断发展,它的底层技术区块链也逐步走进公众视野,引起大众注意.本文用不到50行的Python代码构建最小的数据区块链,简单介绍了区块链去中心化的结构与其实现原理. 尽管一些人 ...
- Postgresql 数据库错误修复v0.1
PS. 查询\nebula_boh\logs\BOHInterfaceLog.log 日志, 一般数据库文件损坏的日志 有 “UncategorizedSQLException” 或 “zero pa ...
- LAMP安装教程
LAMP环境配置安装注意安装步骤及说明事项. Linux + apache+mysql+php 附件: 1. 访问ftp报错 解决: 关闭selinux vi /etc/selinux/config ...
- chat.css
*, *:before, *:after { box-sizing: border-box;}body, html { height: 100%; overflow: hidden;}body, ul ...