字符集
在计算机的世界里,我们需要表示太多太多的字符,为了计算机能够正确的显示这些字符,我们将这些字符编码,使得字符和一系列的代号一一对应。当我们的系统按照一种编码方式去读取一个文件的时候,会自动的将里面的编码转换成相应的字符显示在屏幕上。(我们这里并不讨论如何将字符在显示器上通过点阵的方式显示的这个过程)
中文由于其字符数多,其编码方式自然比西方的字符复杂。所以在编写代码,软件使用的过程中,我们经常碰到中文乱码的相关问题。
如果一个页面是UTF-8的编码(浏览器会首先根据接受到的html自动检测其编码),这个时候如果我们强行以GB2312的编码来解析页面的话就会显示乱码。
因为我们请求一个网址,服务器返回的内容是以指定字符集编码的字节传到浏览器端的,浏览器再按照一定的编码方式去解析这些字节。但是如果传来的正确的字节的编码方式和你解析字节的编码方式不一致,那么就会乱码。
我们碰到的乱码往往是下面的情况
1.一种编码的文件以另一种编码的方式去解析读取,这样肯定出现乱码,这在我们的操作系统里打开文件的时候经常出现。
2.以错误的编码方式对传过来的字节流进行了解码。所以得到了错误的unicode字符串。
3.以和控制台不一致的编码对正确的unicode字符串进行编码,并送至控制台显示。会出现乱码。
介绍几种日常使用中经常碰到的编码:
在我们的日常使用中,我们会碰到iso 8859-1,gb2312,gbk,gb18030,big5,unicode等字符集或者说字符编码,这些都是同一个层次的概念,有些同学可能会问,那UTF-8,UTF-16呢?其实unicode是比较特殊的,虽然通过unicode编码,每一个字符对应一个唯一编码,但是其在计算机上的实现方式却可以有好几种,Unicode的实现方式称为Unicode转换格式(Unicode Translation Format,简称为UTF),所以说UTF-8或者UTF-16只是Unicode编码的一种实现方式。下面我们单独对几个编码进行讲解下:
1.ISO 8859-1
正式编号为ISO/IEC 8859-1:1998,又称Latin-1或“西欧语言”,是国际标准化组织内ISO/IEC 8859的第一个8位字符集。它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个字母及符号,藉以供使用附加符号的拉丁字母语言使用。曾推出过 ISO 8859-1:1987 版。ISO-8859-1是单字节编码。
2.GBK(国标扩展)
全名为汉字内码扩展规范,英文名Chinese Internal Code Specification。K 即是“扩展”所对应的汉语拼音(KuoZhan11)中“扩”字的声母。GBK是对GB2312的扩展,这样GBK在支持简体中文的同时也支持繁体中文。现时中华人民共和国官方强制使用GB18030标准.使用了双字节编码
3.Unicode(是一种世界上所有字符的编码方式。它没有规定的存储方式)
在java或者javascript中我们构造一个“中文”的unicode串的时候一般是使用”u4E2Du6587”来表示,占两个字节
UTF-8(Unicode Transformation Format - 8 bit 的缩写)则是可变长字符编码,可以使用 1~4 个字节表示一个字符,可根据不同的符号而变化字节长度。比如一般的英文字符只需要一个字节,而中文则每个字符占用三个字节。UTF-8 是 Unicode 的一种实现方式
UTF-16两个字节为一个编码单元(固定的),所以从字节的角度来看无法和ASCII实现兼容,且UTF-16存在大尾序和小尾序的两种不同的存储形式。UTF-8 是 Unicode 的一种实现方式
4.GB2312编码:适用于汉字处理、汉字通信等系统之间的信息交换
5.ASCII编码:是对英语字符和二进制之间的关系做的统一规定
正确的字节序列按照错误的编码方式解码成字符或正确的字符被错误的编码成字节序列导致信息的丢失,然后不管如何解码都无法恢复
什么是编解码
字符串在Python内部的表示是unicode编码
因此,在做编码转换时,通常需要以unicode作为中间编码
即先将其他编码的字符串解码(decode)成unicode
再从unicode编码(encode)成另一种编码
举例
str1.decode('gb2312'),表示将gb2312编码的字符串str1转换成unicode编码。 (我觉得这里应该直接写成str1.decode()就可以了)
str1.encode('gb2312'),表示将unicode编码的字符串str1再转换成gb2312编码。
在某些IDE中,字符串的输出总是出现乱码,甚至错误,其实是由于IDE的结果输出控制台自身不能显示字符串的编码,而不是程序本身的问题。
如在UliPad中运行如下代码:
中文"
print s
会提示: UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)。
这是因为UliPad在英文WindowsXP上的控制台信息输出窗口是按照ascii编码输出的(英文系统的默认编码是ascii),而上面代码中的字符串是Unicode编码的,所以输出时产生了错误。
将最后一句改为:print s.encode('gb2312') 则能正确输出“中文”两个字。
若最后一句改为:print s.encode('utf8') 则输出:xe4xb8xadxe6x96x87,
这是控制台信息输出窗口按照ascii编码输出utf8编码的字符串的结果。
代码中字符串的默认编码与代码文件本身的编码一致
如: s='中文' 如果是在utf8的文件中,该字符串就是utf8编码,如果是在gb2312的文件中,则其编码为gb2312。这种情况下,要进行编码转换,通常是以Unicode作为中间编码进行转换的(仅python2),即先将其他编码的字符串解码(decode)成 Unicode,再从 Unicode编码(encode)成另一种编码
如果字符串已经是 Unicode 编码了,那么就不需要进行 decode 进行解码转换了,直接用 encode 就可以编码成你所需要的编码。值得注意的是:对 Unicode 进行编码和对 str 进行编码都是错误的
具体的说就是:如果在UTF-8文件中,则这个字符串就是 UTF-8编码的。它的编码取决于当前的文本编码。当然了,GB2312文本的编码就是GB2312。要在同一个文本中进行两种编码的输出等操作就必须进行编码的转换,先用decode将文本原来的编码转换成Unicode,再用encode将编码转换成需要转换成的编码。
eg:
由于内置函数 open() 打开文件时,read() 读取的是 str,读取后需要使用正确的编码格式进行 decode()。write() 写入时,如果参数是 Unicode,则需要使用你希望写入的编码进行 encode(),如果是其他编码格式的 str,则需要先用该 str 的编码进行 decode(),转成 Unicode 后再使用写入的编码进行 encode()。如果直接将 Unicode 作为参数传入 write() ,python 将先使用源代码文件声明的字符编码进行编码然后写入。
# coding: UTF-8
fp1 = open('test.txt','r')
info1 = fp1.read()
已知是 GBK 编码,解码成 Unicode
tmp = info1.decode('GBK')
fp2 = open('test.txt','w')
编码成 UTF-8 编码的 str
info2 = tmp.encode('UTF-8')
fp2.write(info2)
fp2.close()
获得系统的默认编码
指定特定的编码方式
如果字符串是这样定义: s=u'中文' 则该字符串的编码就被指定为unicode了,即python的内部编码,而与代码文件本身的编码无关。
对于这种情况做编码转换,只需要直接使用encode方法将其转换成指定编码即可。 如果一个字符串已经是unicode了,再进行解码则将出错对其编码方式是否为unicode进行判断:
isinstance(s,unicode) #用来判断是否为unicode,判断s字符串是否为Unicode,如果是返回True,不是返回False
python2中的str类型和unicode类型
在 python 源代码文件中,如果你有用到非ASCII字符,则需要在文件头部进行字符编码的声明,声明如下:
# code: UTF-8
因为python 只检查 #、coding 和编码字符串,所以你可能会见到下面的声明方式,这是为了美观等原因:
#-*- coding: UTF-8 -*-
在Python中有两个和字符很相关的类型,一个是str类型,一个是unicode类型。
这两种类型的对象都是sequece序列,其中str是字节序列,而unicode是字符序列
在2.x版本的python中,默认定义的字符串是str类型
比如这么定义
中文”
us=u”中文”
这样的源码是保存在源码文件中的,其实文件保存在磁盘上的时候都是二进制的字节编码 E4 B8 AD E6 96 87
只有一定的软件比如文件编辑器打开文件对这些文本的二进制编码进行正确的解码后才能在软件中显示正确,被人们看懂
那么在运行的时候是什么样的状态呢?
1、首先python的运行环境会检测你的源码的编码方式,utf-8
2、由于s的类型是str类型(即字节序列),那么我们需要将”中文”从源码文件中按照utf-8读取成字节序列,那么s在运行的时候就是找个字节序列,其长度len(s)为6(因为utf-8中一个中文需要3个字节E4 B8 AD E6 96 87来表示);而us是unicode字符串(即字符序列),那么在运行的时候,us是“中”和“国”这两个字符的unicode字符u4E2D u6587,其长度是2
python2/3的encode和decode函数区别
python2中,使用decode()和encode()来进行解码和编码,以unicode类型作为中间类型。
其中encode()是将某个unicode字符串按照一定的编码方式编码成字节序列
而decode()是一个反过程,将一个字节序列按照一定的编码方式解码成unicode字符串
即
str ---------> unicode --------->str
u = u'中文' #unicode对象u
gb2312_str = u.encode('gb2312') #gb2312编码字符串
gbk_str = u.encode('gbk') #gbk编码字符串
utf8_str = u.encode('utf-8') #utf-8编码字符串
gb2312_u = gb2312_str.decode('gb2312') #gb2312编码的unicode
utf8_u = gb2312_str.decode('utf-8') #utf-8编码的unicode,此处因为编解码方法不一致将导致无法还原原unicode类型
str.encode(e) 是和 unicode(str).encode(e)是一样的,python底层做的时候也是确实这么做的
python这么实现主要是为了方式当某个对象不确定是str还是unicode类型的时候,那么用encode函数总是不会出现错误
python2编解码处理给人的感觉是较复杂
于是在python3中取消了unicode类型代替以unicode编码的字符串类型str
所有的str类型的字符串默认就是unicode字符串,字节数组则可以通过bytes类型来表示
【过程:1、字符(str)通过特定的编码方式编码成二进制(bytes),2、将二进制以同样的编码方式再解码成字符】
举例:
chinese是一个正确的"ab中国"的Unicode字符串,即chinese=u'ab中国'
print(chinese)
这句会将chinese按照系统默认的编码encode成字节流送到输出流里,
然后终端里会对输出的流里的字节按照终端的编码进行decode得到字符
str和bytes关系如下:
bytes --------->str