足球盘口软件
当前位置: 足球盘口软件 > 前端 >
PIL创建文字图片,python编码总结

PIL库中包含了很多模块,恰当地利用这些模块可以做许多图像处理方面的工作。

 

字符集和编码简介

在编程中常常可以见到各种字符集和编码,包括ASCII,MBCS,Unicode等字符集。确切的说,其实字符集和编码是两个不同的概念,只是有些地方有重合罢了。对于ASCII,MBCS等字符集,基本上一个字符集方案只采用一种编码方案,而对于Unicode,字符集和编码方案是明确区分的。

python编码总结(编码类型、格式、转码),python编码

本文详细总结了python编码。分享给大家供大家参考,具体如下:

【所谓unicode】

unicode是一种类似于符号集的抽象编码,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。也就是它只是一种内部表示,不能直接保存。所以存储时需要规定一种存储形式,比如utf-8和utf-16等。理论上unicode是一种能够容纳全世界所有语言文字的编码方案。(其他编码格式不再多说)

【所谓GB码】

GB就是“国标”的意思,即:中华人民共和国国家标准。GB码是面向汉字的编码,包括GB2312(GB2312-80),GBK,GB18030,表示范围从小到大递增,而且基本是向下兼容的。此外经常遇到一种叫做CP936的编码,实际上可以大概看做是GBK。

【判断编码】

1、使用isinstance(s, str)来判断一个字符串是否为一般字符串(str为ascii类型的字符串,utf-8、utf-16、GB2312、GBK等都是ascii类型的字符串);

使用isinstance(s, unicode)来判断一个字符串是否为unicode编码形式的字符串(unicode编码的字符串才是unicode类型的字符串)。

2、使用type()或者.__class__

在编码正确的情况下:

例如:stra = "中", 则使用type(stra)的结果是<type 'str'>,表明为ascii类型字符串;

例如:strb = u"中", 则使用type(strb)的结果是<type 'unicode'>,表明为unicode类型字符串。

tmp_str = 'tmp_str'
print tmp_str.__class__   #<type 'str'>
print type(tmp_str)    #<type 'str'>
print type(tmp_str).__name__ #str
tmp_str = u'tmp_str'
print tmp_str.__class__   #<type 'unicode'>
print type(tmp_str)    #<type 'unicode'>
print type(tmp_str).__name__ #unicode

3、最好的办法是使用chardet判断,特别是在web相关的操作中,例如抓取html页面内容时,页面的charset标签只是标示编码,有时候不对,而且页面内容中一些中文可能超出了标示编码的范围,此时用charset检测最为方便准确。

(1)安装办法:下载chardet后,将解压得到的chardet文件夹放在Python安装目录的Libsite-packages目录下,在程序中使用import chardet即可。

(2)使用办法1:检测全部内容判断编码

import urllib2
import chardet
res = urllib2.urlopen('http://www.bkjia.com')
res_cont = res.read()
res.close()
print chardet.detect(res_cont) #{'confidence': 0.99, 'encoding': 'utf-8'}

detect函数返回值为一个包含2个键值对的字典,第一个是检测置信度,第二个就是检测到的编码形式。

(3)使用办法2:检测部分内容判断编码,提高速度

import urllib2
from chardet.universaldetector import UniversalDetector
res = urllib2.urlopen('http://www.bkjia.com')
detector = UniversalDetector()
for line in res.readlines():
 #detect untill reach threshold
 detector.feed(line)
 if detector.done:
  break
detector.close()
res.close()
print detector.result
#{'confidence': 0.99, 'encoding': 'utf-8'}

【转换编码】

1、从具体的编码(ISO-8859-1[ASCII码],utf-8,utf-16,GBK,GB2312等)转换为unicode,直接使用unicode(s, charset)或者s.decode(charset),其中charset为s的编码(注意unicode在使用decode()时会出错);

#将任意字符串转换为unicode
def to_unicode(s, encoding):
 if isinstance(s, unicode):
  return s
 else:
  return unicode(s, encoding)

注意:这里在decode()的时候,如果遇到非法字符(比如不标准的全角空格xa3xa0,或者xa4x57,真正的全角空格是xa1xa1),就会报错。

解决办法:采用'ignore'模式,即:stra.decode('...', 'ignore').encode('utf-8')。

解释:decode的函数原型是decode([encoding],[errors='strict']),可以用第二个参数控制错误处理的策略。

默认的参数就是strict,代表遇到非法字符时抛出异常;如果设置为ignore,则会忽略非法字符;如果设置为replace,则会用?取代非法字符;如果设置为xmlcharrefreplace,则使用XML的字符引用。

2、从unicode转换为具体的编码,也是直接用s.encode(charset),其中s为unicode编码,charset为具体的编码(注意非unicode在使用encode()时会出错);

3、自然地,从一种具体编码转换为另一种具体编码,就可以先decode成unicode再encode成最终编码了。

【python命令行编码(系统编码)】

用python自带的locale模块来检测命令行的默认编码(也就是系统的编码)和设置命令行编码:

import locale
#get coding type
print locale.getdefaultlocale() #('zh_CN', 'cp936')
#set coding type
locale.setlocale(locale.LC_ALL, locale='zh_CN.GB2312')
print locale.getlocale() #('zh_CN', 'gb2312')

表明当前系统的内部编码是cp936,近似于GBK。实际上中文XP和WIN7的系统内部编码都是cp936(GBK)。

【python代码中的编码】

下面是我用来生成字母或字符串测试图片而写的类及测试代码。

使用codecs模块,在Python中完成字符编码

 

字符的编码是按照某种规则在单字节字符和多字节字符之间进行转换的某种方法。从单字节到多字节叫做decoding,从多字节到单字节叫做encoding。在这些规则中经常用到的无非是UTF-8和GB2312两种。

 

在Python中,codecs模块提供了实现这些规则的方法,通过模块公开的方法我们能够方便地获取某种编码方式的Encoder和 Decoder工厂函数(Factory function),以及StreamReader、StreamWriter和StreamReaderWriter类。

 

使用“import codecs”导入codecs模块。

 

codecs模块中重要的函数之一是lookup,它只有一个参数encoding,指的是编码方式的名称,即utf-8或者gb2312等等。如下示例:

>>> import codecs>>> t = codecs.lookup("utf-8")>>> print t(<built-in function utf_8_encode>, <function decode at 0x00AA25B0>, <class encodings.utf_8.StreamReader at 0x00AA0720>, <class encodings.utf_8.StreamWriter at 0x00AA06F0>)>>> encoder = t[0]>>> decoder = t[1]>>> StreamReader = t[2]>>> StreamWriter = t[3]

lookup函数返回一个包含四个元素的TUPLE,其中t[0]是encoder的函数引用,t[1]是decoder的函数引用,t[2] 是UTF-8编码方式的StreamReader类对象引用,t[3]是UTF-8编码方式的StreamWriter类对象引用相信对Python熟悉的你肯定知道接下来该怎么用它们了。

 

codecs模块还提供了方便程序员使用的单独函数,以简化对lookup的调用。它们是:

``

  • getencoder(encoding)
  • getdecoder(encoding)
  • getreader(encoding)
  • getwriter(encoding)

如果我们只是想获取一种utf-8编码的encoder方法,那么只需要这样做:

>>> encoder = codecs.getencoder("utf-8")

 

另外,对于StreamReader和StreamWriter的简化, codecs模块提供一个open方法。相对于built-in对象File的open方法,前者多了三个参数encoding, errors, buffering。这三个参数都是可选参数,但是对于应用来说,需要明确指定encoding的值,而errors和buffering使用默认值即可。使用方法如下:

>>> fin = codecs.open("e:\mycomputer.txt", "r", "utf-8")>>> print fin.readline()这是我的电脑>>> fin.close()

 

总结一下,codecs模块为我们解决的字符编码的处理提供了lookup方法,它接受一个字符编码名称的参数,并返回指定字符编码对应的 encoder、decoder、StreamReader和StreamWriter的函数对象和类对象的引用。为了简化对lookup方法的调用, codecs还提供了getencoder(encoding)、getdecoder(encoding)、getreader(encoding)和 getwriter(encoding)方法;进一步,简化对特定字符编码的StreamReader、StreamWriter和 StreamReaderWriter的访问,codecs更直接地提供了open方法,通过encoding参数传递字符编码名称,即可获得对 encoder和decoder的双向服务。

 

 

 

 

1 ASCII

其中ASCII标准本身就规定了字符和字符编码方式,采用单字节编码,总共可以编码128个字符,如空格的编码是32,小写字母a是97,所以ASCII既是字符集又是编码方案。
计算机世界里一开始只有英文,而单字节可以表示256个不同的字符,可以表示所有的英文字符和许多的控制符号。不过ASCII只用到了其中的一半。

1、python代码中的字符串在未被指定编码的情况下,默认编码与代码文件本身的编码一致。举个例子:str

'中文'这个字符串,如果是在utf8编码的代码文件中,该字符串就是utf8编码;如果是在gb2312的文件中,该字符串就是gb2312编码。那么代码文件本身的编码怎么知道呢?

(1)自己指定代码文件的编码:在代码文件的头部加上“#-*- coding:utf-8 -*-”来声明该代码文件为utf-8编码。此时未被指定编码的字符串的编码都变成了utf-8。

(2)在没有指定代码文件的编码时,创建代码文件时使用的是python默认采用的编码(一般来说是ascii码,在windows中实际保存为cp936(GBK)编码)。通过sys.getdefaultencoding()和sys.setdefaultencoding('...')来获取和设置该默认编码。

import sys
reload(sys)
print sys.getdefaultencoding() #ascii
sys.setdefaultencoding('utf-8')
print sys.getdefaultencoding() #utf-8

结合(1)和(2)做个试验:指定代码文件编码为utf-8时,用notepad++打开显示的是utf-8无DOM编码;未指定代码文件编码时,用notepad++打开显示的是ANSI编码(压缩编码,默认的保存编码形式)。

图片 1

(3)如何永久地将python默认采用的编码设置为utf-8呢?有2种方法:

第一个方法<不推荐>:编辑site.py,修改setencoding()函数,强制设置为 utf-8;

第二个方法<推荐>:增加一个名为 sitecustomize.py的文件,存放在安装目录下的Libsite-packages目录下

sitecustomize.py是在site.py被import执行的,因为 sys.setdefaultencoding()是在site.py的结尾处被删除的,所以可以在 sitecustomize.py使用 sys.setdefaultencoding()。

2、python代码中的字符串如果被指定了编码,举个例子:str = u'中文',该字符串的编码被指定为unicode(即python的内部编码)。

(1)这里有个误区需要注意!假如在py文件中有如下代码:

stra = u"中"
print stra.encode("gbk")

按上面说的stra是unicode形式,直接encode称gbk编码应该没问题啊?但是实际执行时会报错“UnicodeEncodeError: 'gbk' codec can't encode character u'xd6' in position 0: illegal multibyte sequence”。

原因在于:python解释器在导入python代码文件并执行时,会先查看文件头有没有编码声明(例如#coding:gbk等)。如果发现声明,会将文件中的字符串都先解释成unicode的形式(这里先用默认编码gbk(cp936)将stra解码成unicode编码'd6d0'后保存),之后执行stra.encode('gbk')时,由于stra已经是unicode编码且'd6d0'在gbk的编码范围内,所以编码不会出现错误;如果文件头没有编码声明,则不会进行上述过程中的解码操作(这里就直接使用stra的unicode编码'd6'),之后执行stra.encode('gbk')时,由于'd6'不在gbk的编码范围所以报错。

(2)为避免这种类型的错误,最好在代码文件头上声明编码,或者麻烦点每次使用setdefaultencoding()。

(3)总的来说就是unicode是python解释器的内码,所有代码文件在导入并执行时,python解释器会先将字符串使用你指定的编码形式解码成unicode,然后再进行各种操作。所以不管是对字符串的操作,还是正则表达式,还是读写文件等等最好都通过unicode来进行。

【python中其他编码】

文件系统的编码:sys.getfilesystemencoding()
终端的输入编码:sys.stdin.encoding
终端的输出编码:sys.stdout.encoding

更多关于Python相关内容感兴趣的读者可查看本站专题:《Python编码操作技巧总结》、《Python图片操作技巧总结》、《Python数据结构与算法教程》、《Python Socket编程技巧总结》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》及《Python文件与目录操作技巧汇总》

希望本文所述对大家Python程序设计有所帮助。

本文详细总结了python编码。分享给大家供大家参考,具体如下: 【所谓unicode】 unicode是一...

主要用到的模块:

PIL.Image,PIL.ImageDraw,PIL.ImageFont

PIL.Image用来生成一个空的图片,ImageDraw用来在空图片上画图及写字符,ImageFont则是创建需要使用到的字体

#-*- coding:gb2312 -*-
from PIL import Image,ImageDraw,ImageFont,ImageOps
import numpy as np
import random


class LetterImage():

    def __init__(self,fontFile='',imgSize=(0,0),imgMode='RGB',bg_color=(0,0,0),fg_color=(255,255,255),fontsize=20):
        self.imgSize = imgSize
        self.imgMode = imgMode
        self.fontsize = fontsize
        self.bg_color = bg_color
        self.fg_color = fg_color
#         self.font = ImageFont.load('车牌字体.ttf')
        if ''==fontFile:
            self.font = ImageFont.truetype('DIN1451.ttf', fontsize)
        else:
            self.font = ImageFont.truetype(fontFile,fontsize)


    def GenLetterImage(self,letters):
        '''Generate the Image of letters''' 
        self.letters = letters
        (self.letterWidth,self.letterHeight) = self.font.getsize(letters)
        if self.imgSize==(0,0):
            self.imgSize=(self.letterWidth+2,self.letterHeight+2)
        self.imgWidth,self.imgHeight=self.imgSize
        self.img = Image.new(self.imgMode, self.imgSize, self.bg_color)
        self.drawBrush = ImageDraw.Draw(self.img)
        textY0 = (self.imgHeight-self.letterHeight+1)/2
        textY0 = int(textY0)
        textX0 = int((self.imgWidth-self.letterWidth+1)/2)
        print 'text location:',(textX0,textY0)
        print 'text size (width,height):',self.letterWidth,self.letterHeight
        print 'img size(width,height):',self.imgSize
#         if textX0<0 or textY0<0:
#             raise Exception('size error text location x0:%d,y0:%d'%(textX0,textY0))
        self.drawBrush.text((textX0,textY0), self.letters, fill=self.fg_color,font=self.font)

    def SaveImg(self,saveName=''):
        if ''==saveName.strip():
            saveName = str(self.letters.encode('gb2312'))+'.png'
        fileName,file_format = saveName.split('.')
        fileName+='_'+str(self.fontsize)+'.'+file_format
        print fileName,file_format
        self.img.save(fileName, file_format)

    def Show(self):
        self.img.show()

def clearpictures():
    import os
    png = os.listdir(os.curdir)
    for i in png:
        if os.path.splitext(i)[1]==".png":
            os.remove(i)


if __name__=='__main__':
    letterList = []
    letterList.append(LetterImage(bg_color=(0,0,255),fontsize=10))
    letterList.append(LetterImage(fontFile='',bg_color=(0,0,255),fontsize=400))
    letter=[u'u',u'v']
    num_letter = 2

    svd_u=[]
    svd_s=[]
    svd_v=[]
    import cv2
    mergeImg = np.zeros((470,444))
    npareiImg =[]
    for i in range(num_letter):
        letterList[i].GenLetterImage(letter[i])
#         letterList[i].Show()
#         letterList[i].SaveImg()
        grayImg = ImageOps.grayscale(letterList[i].img)
        grayImg = grayImg.resize((222,470),resample=Image.BICUBIC)
        npareiImg.append( np.asarray(grayImg))
        cv2.namedWindow('%s'%i)
        cv2.imshow('%s'%i, npareiImg[i])
        mergeImg[0:470,i*222:(i+1)*222]=npareiImg[i]
        u,s,v=np.linalg.svd(npareiImg[i])
        print 'u and img 's shape',u.shape,npareiImg[i].shape
        svd_u.append(u)
        svd_v.append(v)
        svd_s.append(s)

#     mergeImgNp=Image.fromarray(mergeImg)#, mode)
#     mergeImgNp.show()
    uDifNorm=np.linalg.norm(svd_u[0]-svd_u[1])
    print uDifNorm
    vDifNorm = np.linalg.norm(svd_v[0]-svd_v[1])
    print vDifNorm
    sDifNorm = np.linalg.norm(svd_s[0]-svd_s[1])
    print sDifNorm 
    ou_norm = np.linalg.norm(np.asarray(npareiImg[0])-np.asarray(npareiImg[1]))
    print ou_norm
    f=open('record.txt','a')
    lines=[]
    lines.append('letters: %s,%s'%(letter[0],letter[1]))
    lines.append('SVD u diff norm:t%f'%uDifNorm)
    lines.append('SVD v diff norm:t%f'%vDifNorm)
    lines.append('SVD s diff norm:t%f'%sDifNorm)
    lines.append('Ou norm:        t%f'%ou_norm)
    str_to_write='n'.join(lines)+'n'
    print str_to_write
    f.write(str_to_write)
    f.close()
    cv2.waitKey()

python自然语言编码转换模块codecs介绍

python对多国语言的处理是支持的很好的,它可以处理现在任意编码的字符,这里深入的研究一下python对多种不同语言的处理。

有一点需要清楚的是,当python要做编码转换的时候,会借助于内部的编码,转换过程是这样的:

复制代码代码如下:

原有编码 -> 内部编码 -> 目的编码

python的内部是使用unicode来处理的,但是unicode的使用需要考虑的是它的编码格式有两种,一是UCS-2,它一共有65536个码位,另一种是UCS-4,它有2147483648g个码位。对于这两种格式,python都是支持的,这个是在编译时通过--enable-unicode=ucs2或--enable-unicode=ucs4来指定的。那么我们自己默认安装的python有的什么编码怎么来确定呢?有一个办法,就是通过sys.maxunicode的值来判断: 

复制代码代码如下:

import sys
print sys.maxunicode

 

如果输出的值为65535,那么就是UCS-2,如果输出是1114111就是UCS-4编码。
我们要认识到一点:当一个字符串转换为内部编码后,它就不是str类型了!它是unicode类型:

复制代码代码如下:

a = "风卷残云"
print type(a)
b = a.unicode(a, "gb2312")
print type(b)

输出:

复制代码代码如下:

<type 'str'>
<type 'unicode'>

这个时候b可以方便的任意转换为其他编码,比如转换为utf-8:

复制代码代码如下:

c = b.encode("utf-8")
print c

c输出的东西看起来是乱码,那就对了,因为是utf-8的字符串。

 

好了,该说说codecs模块了,它和我上面说的概念是密切相关的。codecs专门用作编码转换,当然,其实通过它的接口是可以扩展到其他关于代码方面的转换的,这个东西这里不涉及。

复制代码代码如下:

#-*- encoding: gb2312 -*-
import codecs, sys

 

print '-'*60
# 创建gb2312编码器
look  = codecs.lookup("gb2312")
# 创建utf-8编码器
look2 = codecs.lookup("utf-8")

a = "我爱北京天安门"

print len(a), a
# 把a编码为内部的unicode, 但为什么方法名为decode呢,我的理解是把gb2312的字符串解码为unicode
b = look.decode(a)
# 返回的b[0]是数据,b[1]是长度,这个时候的类型是unicode了
print b[1], b[0], type(b[0])
# 把内部编码的unicode转换为gb2312编码的字符串,encode方法会返回一个字符串类型
b2 = look.encode(b[0])
# 发现不一样的地方了吧?转换回来之后,字符串长度由14变为了7! 现在的返回的长度才是真正的字数,原来的是字节数
print b2[1], b2[0], type(b2[0])
# 虽然上面返回了字数,但并不意味着用len求b2[0]的长度就是7了,仍然还是14,仅仅是codecs.encode会统计字数
print len(b2[0])

 

上面的代码就是codecs的使用,是最常见的用法。另外还有一个问题就是,如果我们处理的文件里的字符编码是其他类型的呢?这个读取进行做处理也需要特殊的处理的。codecs也提供了方法.

复制代码代码如下:

#-*- encoding: gb2312 -*-
import codecs, sys

 

# 用codecs提供的open方法来指定打开的文件的语言编码,它会在读取的时候自动转换为内部unicode
bfile = codecs.open("dddd.txt", 'r', "big5")
#bfile = open("dddd.txt", 'r')

ss = bfile.read()
bfile.close()
# 输出,这个时候看到的就是转换后的结果。如果使用语言内建的open函数来打开文件,这里看到的必定是乱码
print ss, type(ss)

上面这个处理big5的,可以去找段big5编码的文件试试。

 

 

 

2 MBCS

对于英文来说,128个符号编码已经够用了,然而对于其它语言比如中文,显然就不够了。后来每个语言就制定了一套自己的编码,由于单字节能表示的字符太少,而且同时也需要与ASCII编码保持兼容,所以这些编码纷纷使用了多字节来表示字符,如GBxxx、BIGxxx等等,他们的规则是,如果第一个字节是x80以下,则仍然表示ASCII字符;而如果是x80以上,则跟下一个字节一起(共两个字节)表示一个字符。因此就出现了多字节字符集MBCS(Multi-Byte Character Set)。如GB2312,GBK,GB18030,BIG5等编码都属于MBCS。由于MBCS大都使用2个字节编码,所以有时候也叫作DBCS(Double-Byte Character Set)。我们在Linux系统中看到含有中文的文件编码常常是CP936,那这个其实就是GBK编码了,这个名字的由来是因为IBM曾经发明了一个Code Page的概念,把这些多字节编码收入其中,GBK编码正好位于936页,所以就简称CP936了。
GB2312是中国规定的汉字编码,也可以说是简体中文的字符集编码。
GBK是GB2312的扩展,除了兼容GB2312外,它还能显示繁体中文,还有日文的假名。

上面的测试后部分是对图像做SVD变换的一点实验。

显示的结果(图像已经被resize到统一大小,代码中的字符图像类生成的图像其实会根据字体大小自动设定)

#-*- coding:gb2312 -*- 指定使用中文编码。这样一般不会有错。但有时可能我们需要对部分字符串转换编码,这时我们可利用字符对象的encode、decode方法。encode是对当前字符使用指定的编码方案重新编码。decode是使用指定的编码方案进行解码。两者都是码制的转换,但使用时往往容易弄错。encode其实是对本身为unicode的字符使用指定的字符进行编码,而decode则是使用指定编码将字符解码为unicode编码。所有在使用encode时,如果本身不是unicode码就会出错,在使用decode时,如果不知道本身所使用的编码方案也会出错.

下面是我用来生成字母或字符串测试图片而写的类及测试代...

Python字符串的encode与decode研究心得——解决乱码问题

 

为什么Python使用过程中会出现各式各样的乱码问题,明明是中文字符却显示成“/xe4/xb8/xad/xe6/x96/x87”的形式?为什么会报错“UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)”?本文就来研究一下这个问题。


字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。 

decode的作用是将其他编码的字符串转换成unicode编码,如str1.decode('gb2312'),表示将gb2312编码的字符串str1转换成unicode编码。 

encode的作用是将unicode编码转换成其他编码的字符串,如str2.encode('gb2312'),表示将unicode编码的字符串str2转换成gb2312编码。 

因此,转码的时候一定要先搞明白,字符串str是什么编码,然后decode成unicode,然后再encode成其他编码


代码中字符串的默认编码与代码文件本身的编码一致。 

如:s='中文'

如果是在utf8的文件中,该字符串就是utf8编码,如果是在gb2312的文件中,则其编码为gb2312。这种情况下,要进行编码转换,都需要先用decode方法将其转换成unicode编码,再使用encode方法将其转换成其他编码。通常,在没有指定特定的编码方式时,都是使用的系统默认编码创建的代码文件。 

如果字符串是这样定义:s=u'中文'

则该字符串的编码就被指定为unicode了,即python的内部编码,而与代码文件本身的编码无关。因此,对于这种情况做编码转换,只需要直接使用encode方法将其转换成指定编码即可。


如果一个字符串已经是unicode了,再进行解码则将出错,因此通常要对其编码方式是否为unicode进行判断:

isinstance(s, unicode)  #用来判断是否为unicode 

用非unicode编码形式的str来encode会报错 


 如何获得系统的默认编码? 

#!/usr/bin/env python
#coding=utf-8
import sys
print sys.getdefaultencoding()  

该段程序在英文WindowsXP上输出为:ascii 


在某些IDE中,字符串的输出总是出现乱码,甚至错误,其实是由于IDE的结果输出控制台自身不能显示字符串的编码,而不是程序本身的问题。 

如在UliPad中运行如下代码:

s=u"中文"
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')

则输出:/xe4/xb8/xad/xe6/x96/x87,这是控制台信息输出窗口按照ascii编码输出utf8编码的字符串的结果。


unicode(str,'gb2312')与str.decode('gb2312')是一样的,都是将gb2312编码的str转为unicode编码 

使用str.__class__可以查看str的编码形式


原理说了半天,最后来个包治百病的吧:)

 

[python] view plain copy

 

  1. #!/usr/bin/env python  
  2. #coding=utf-8  
  3. s="中文"  
  4.   
  5. if isinstance(s, unicode):  
  6. #s=u"中文"  
  7.     print s.encode('gb2312')  
  8. else:  
  9. #s="中文"  
  10.     print s.decode('utf-8').encode('gb2312')  

 

 

 

 

 

3 Unicode

而后大家觉得各种编码太多不方便,不如所有语言字符都使用一套字符集来表示,于是就出现了Unicode。Unicode/UCS(Unicode Character Set)标准只是一个字符集标准,但是它并没有规定字符的存储和传输方式。Unicode是一种字符集而不是具体的编码,它主要有3种编码方式:最初Unicode标准使用2个字节表示一个字符,编码方案是UTF-16。还有使用4个字节表示一个字符的编码方案UTF-32。而后来使用英文字符的国家觉得不好,原理一个字符存储的现在变成了2个字符,空间增大了一倍,由此UTF-8编码。UTF-8编码中,英文占一个字节,中文占3个字节。
如上面所提到的,Unicode字符集采用UTF-8,UTF-16等方式进行编码存储。那么这样的话,计算机如何知道文件采用哪种方式编码呢?Unicode规范中又定义,在每个文件最前面加入一个表示编码顺序的字符BOM(Byte Order Mark)。比如石锅拌饭中的‘石’的UTF-16编码是77F3,采用UTF-16方式存储使用2个字节,一个字节是77,一个字节是F3.存储的时候如果77在前面,F3在后面,则称为big endian方式。反之,则是Little endian方式。
你可能听说过UTF-8不需要BOM,这种说法是不对的,只是绝大多数编辑器在没有BOM时都是以UTF-8作为默认编码读取。即使是保存时默认使用ANSI(MBCS)的记事本,在读取文件时也是先使用UTF-8测试编码,如果可以成功解码,则使用UTF-8解码。记事本这个别扭的做法造成了一个BUG:如果你新建文本文件并输入"姹塧"然后使用ANSI(MBCS)保存,再打开就会变成"汉a",你不妨试试 :)。
现在,捋一捋ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。

字母A用ASCII编码是十进制的65,二进制的01000001;
字符0用ASCII编码是十进制的48,二进制的00110000,注意字符'0'和整数0是不同的;
汉字'中'已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101;
你可以猜测,如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是00000000 01000001。

在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:

图片 2

Paste_Image.png

浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:

图片 3

Paste_Image.png

所以你看到很多网页的源码上会有类似<meta charset="UTF-8"/>的信息,表示该网页正是用的UTF-8编码。

python之decode、encode及codecs模块

4 ANSI

此外,还有一种不得不提的是ANSI,ANSI在windows系统中极为常见,其实ANSI是Windows code pages,这个模式根据当前的locale选定具体编码,如果系统locale是简体中文则采用GBK编码,繁体中文为BIG5编码,日文则是JIS编码。
此外windows中喜欢把BOM_UTF16_LE编码称作Unicode, 把BOM_UTF8称作UTF-8。也有人说UTF-8不需要BOM来标示,其实是不对的,这是因为编辑器一般默认使用UTF-8来测试字符编码而已,如果可以成功解码,就用UTF-8进行解码。即便最开始采用的是ANSI保存的,打开文件时还是最先使用UTF-8来解码。比如你用windows的记事本程序新建一个文件,写入“姹塧”并用ANSI编码保存,再次打开文件,会发现“姹塧”会变成“汉a”。

一、先说说编解码问题

编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。

图片 4

Eg:

1
2
str1.decode('gb2312')    #将gb2312编码的字符串转换成unicode编码
str2.encode('gb2312')    #将unicode编码的字符串转换成gb2312编码

python2.7 idle GUI界面打印中文会出现乱码,这是idle本身问题:

图片 5   

cmd界面的python2.7则是正常的:

图片 6

注意事项:

  •  s='中文' 如果是在utf8的文件中,该字符串就是utf8编码,如果是在gb2312的文件中,则其编码为gb2312。这种情况下,要进行编码转换,都需要先用decode方法将其转换成unicode编码,再使用encode方法将其转换成其他编码。
  • 在没有指定特定的编码方式时,都是使用的系统默认编码创建的代码文件。
  • 如果字符串是这样定义: s=u'中文' 则该字符串的编码就被指定为unicode了,即python的内部编码,而与代码文件本身的编码无关。只需要直接使用encode方法将其转换成指定编码即可
  • 如果一个字符串已经是unicode了,再进行解码则将出错,因此通常要对其编码方式是否为unicode进行判断isinstance(s, unicode) #用来判断是否为unicode 

1 Python 编码基础

二、查看文本编码的方式 

1.1 str和unicode

python中有两种数据模型来支持字符串这种数据类型,str和unicode,它们的基类都是basestring。比如s='中文'就是str类型的字符串,而u=u'中文'就是一个unicode类型的字符串。unicode是由str类型的字符串解码后得到,unicode也可以编码成str类型。即

str --> decode --> unicode
unicode --> encode --> str

关于encode和decode,

s.encode不妨理解为s.encodeTo().
s.decode不妨理解为s.decodeFrom().

encode可以指定error handler,以防脚本在执行的时候因为编码问题而中断。

s.encode('gb18030', 'ignore')
s.encode('gb18030', 'replace')

严格来说,str也许应该叫做字节串,它是unicode经过编码后的字节组成的序列,因为对于UTF-8编码的str类型‘中文’,使用len()函数得到的结果是6,因为UTF-8编码的str类型‘中文’实际是'xe4xb8xadxe6x96x87'。
unicode才是真正意义上的字符串,对字节串str使用正确的字符编码进行解码后获得。而对于unicode类型u'中文'(实际是u'u4e2du6587'),使用len()函数得到结果是2。

1. notepad

对于我们经常使用的记事本,“文件”  ->  “另存为”,可查看到当前的编码方式:

图片 7

1.2 头部编码声明

在python源代码文件中如果有用到非ascii字符,比如中文,那么需要在源码文件头部声明源代码字符编码,格式如下:

#-*- coding: utf-8 -*-

这个格式看起来比较复杂,其实python只检查#,coding,编码等字符串,可以简写成#coding:utf-8。

2.notepad++

点击“菜单栏”  ->  “格式”可以查看到:

图片 8

还可直接对其进行转换,转换完成后保存文件。

2 Python2.x常见编码问题

3.UltraEdit

 不同编码的文本,是根据文本的前两个字节来定义其编码格式的,定义如下:

ANSI:        无格式定义; 
Unicode:       前两个字节为FFFE; 
Unicode big endian: 前两字节为FEFF;  
UTF-8:        前两字节为EFBB; 
这样通过前面两个字节就可以判定出文件的具体格式了。

2.1 头部编码声明和文件编码问题

文件头部编码声明决定了python解释器解析源码中的str的编码选择方式,比如头部声明的是utf-8编码,则代码中s='中文',python就会按照utf-8编码格式来解析,通过repr(s)可以看到字符编码是"xe4xb8xadxe6x96x87",如果头部声明的编码是gbk编码,则python会对s采用gbk编码解析,结果是"xd6xd0xcexc4"。
需要注意的是,文件本身的编码要跟文件头部声明编码一致,不然就会出现问题。文件本身的编码在Linux下面可以在vim下用命令set fenc来查看。如果文件本身编码是gbk,而源码文件头部声明的编码是utf-8,这样如果源码中有中文就会有问题了,因为本身中文str存储是按照gbk编码来的,而python在解析str的时候又以为是utf-8编码,这样就会报SyntaxError: (unicode error) 'utf-8' codec can't decode byte错误。

三、系统中常见的编码方式

2.2 默认编码问题

下面看个python默认编码导致的问题:

#-*-coding: utf-8 -*-
import sys
print sys.getdefaultencoding()
u = u"中文"
print 'repr(u) is', repr(u)

s = "中文"
print 'repr(s) is', repr(s)

u2 = s.decode("utf-8")
print 'u2 is', repr(u2)

#s2 = u.decode("utf-8") #编码错误
#u2 = s.encode("utf-8") #编码错误

ascii
repr(u) is u'u4e2du6587'
repr(s) is 'xe4xb8xadxe6x96x87'
u2 is u'u4e2du6587'

Traceback (most recent call last):
  File "C:/Users/kaicz/Desktop/bianma.py", line 13, in <module>
    s2 = u.decode("utf-8")
  File "C:Python27libencodingsutf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

Traceback (most recent call last):
  File "C:/Users/kaicz/Desktop/bianma.py", line 14, in <module>
    u2 = s.encode("utf-8")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

注意实例中注释掉的2行代码,对于unicode最好不要直接调用decode,str最好不要直接调用encode方法。因为如果是直接调用,则相当于u.encode(default_encoding).decode('utf-8'),default_encoding是python的unicode实现中用的默认编码,即sys.getdefaultencoding()得到的编码,如果你没有设置过,那么默认编码就是ascii,如果你的unicode本身超出了ascii编码范围就会报错。同理,如果对str直接调用encode方法,那么默认会先对str进行解码,即s.decode(default_encoding).encode('utf-8'),如果str本身是中文,而default_encoding是ascii的话,解码就会出错,从而导致上面这两行会分别报UnicodeEncodeError: 'ascii' codec can't encode characters in position...错误和UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position...错误。
上面例子中注释掉的两行代码如果执行就会报错,当然,如果本身str或者unicode都在ascii编码范围,就没有问题。比如s='abc'; s.encode('utf-8')就不会有问题,语句执行后会返回一个跟s的id不同的str。
那如果要解决实例1中的问题,有两种方法,其一是明确指定编码,如下所示,

#coding: utf-8
u = u"中文"
print repr(u) # u'u4e2du6587'

s = "中文"
print repr(s) # 'xe4xb8xadxe6x96x87'

u2 = s.decode("utf-8")
print repr(u2) # u'u4e2du6587'

s2 = u.encode("utf-8").decode("utf-8")  # OK                                    
u2 = s.decode("utf8").encode("utf-8")   # OK

第二种方法就是更改python的默认编码为文件编码格式,如下所示(这里要reload sys模块,是因为python初始化后删除了setdefaultencoding方法):

#coding:utf-8                                                                   

import sys 
reload(sys)
sys.setdefaultencoding("utf-8") #更改默认编码为utf-8

u = u"中文"
print repr(u) # u'u4e2du6587'

s = "中文"
print repr(s) # 'xe4xb8xadxe6x96x87'

u2 = s.decode("utf-8")
print repr(u2) # u'u4e2du6587'

s2 = u.decode("utf-8")
u2 = s.encode("utf-8")

市面上有些库,例如BeautifulSoup,有时候不正确地使用了ascii来解决中文编码,要修正这种行为在文件头加上,

import sys
reload(sys) #载入setdefaultencoding方法
sys.setdefaultencoding('utf-8')

1.ASCII编码

上世纪70年代,美国国家标准协会(American National Standard Institute , ANSI )制订了ASCII码(American Standard Code for Information Interchange,美国标准信息交换码)

使用7 位二进制数共128个组合来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。

第0~32号及第127号(共34个)是控制字符或通讯专用字符,如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BEL(振铃)等

第33~126号(共94个)是字符,其中第48~57号为0~9十个阿拉伯数字;65~90号为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。

最高位(b7)用作奇偶校验位,所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种:

  • 奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1
  • 偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1

2.3 读写文件编码

采用python的open()方法打开文件时,read()读取的是str,编码就是文件本身的编码。而调用write()写文件时,如果参数是unicode,则需要用指定编码encode,如果write()参数是unicode而且没有指定编码,则采用python默认编码encode后再写入。

#coding:utf-8                                                                   
f = open("testfile")
s = f.read()
f.close()
print type(s) # <type 'str'>

u = s.decode("utf-8") #testfile是utf-8编码
f = open("testfile", "w")
f.write(u.encode("gbk")) #以gbk编码写入,testfile为gbk编码
f.close()

此外,python codecs模块提供了一个open()方法,可以指定编码打开文件,使用这个方法打开文件读取返回是unicode。写入时,如果write参数是unicode,则使用打开文件时的编码写入,如果是str,则先使用默认编码成unicode后再以打开文件的编码写入(这里需要注意如果str是中文,而默认编码sys.getdefaultencoding()是ascii的话就会报解码错误)。

#coding:gbk
import codecs

f = codecs.open('testfile', encoding='utf-8')
u = f.read()
f.close()
print type(u) # <type 'unicode'>

f = codecs.open('testfile', 'a', encoding='utf-8')
f.write(u) #写入unicode

# 写入gbk编码的str,自动进行解码编码操作
s = '汉'
print repr(s) # 'xbaxba'
# 这里会先将GBK编码的str解码为unicode再编码为UTF-8写入
#f.write(s) #默认编码为ascii时,这会报解码错误。
f.close()

2.扩展的ASCII编码

一个字节中的后7位总共只能表示128个不同的字符,英语用这些字符已经足够了,可是要表示其他语言却是不够。比如,在法语中,字母上方有注音的符号,就无法用ASCII表示。于是,一些国家就利用了字节中闲置的最高位编入新的符号。这样一来,就可以表示最多256个符号,这就是扩展ASCII 码,所以现在有7位和8位的两种ASCII码,扩展的ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。但是,不管怎样,0~127表示的字符是一样的,不同的只是128~255.

2.4 与编码相关的方法

#coding: gbk
import sys
import locale

def p(f):
    print '%s.%s(): %s' %(f.__module__, f.__name__, f())
# 返回当前系统所使用的默认字符编码
p(sys.getdefaultencoding)

# 返回用于转换Unicode文件名至系统文件名所使用的编码
p(sys.getfilesystemencoding)

# 获取默认的区域设置并返回元祖(语言, 编码)
p(locale.getdefaultlocale)

# 返回用户设定的文本数据编码
# 文档提到this function only returns a guess
p(locale.getpreferredencoding)

# xbaxba是'汉'的GBK编码
# mbcs是不推荐使用的编码,这里仅作测试表明为什么不应该用
print r"'xbaxba'.decode('mbcs'):", repr('xbaxba'.decode('mbcs'))

在笔者的Windows上的结果(区域设置为中文(简体, 中国))

sys.getdefaultencoding(): ascii
sys.getfilesystemencoding(): mbcs
locale.getdefaultlocale(): ('zh_CN', 'cp936')
locale.getpreferredencoding(): cp936
'xbaxba'.decode('mbcs'): u'u6c49'

3.ANSI编码

也是美国国家标准协会(American National Standard Institute , ANSI )制订的标准。为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。

不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。

  • 在简体中文系统下,ANSI 编码代表 GB2312 编码
  • 在日文操作系统下,ANSI 编码代表 JIS 编码

不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。

ANSI编码表示英文字符时用一个字节,表示中文用两个字节,而unicode不管表示英文字符还是中文都是用两个字节来表示。

3 python开发过程中涉及到的编码

在开发python程序的过程中,会涉及到三个方面的编码:

  • Python程序文件的编码;
  • Python程序运行时环境(IDE)的编码;
  • Python程序读取外部文件、网页的编码;

4.Unicode编码

Unicode字符集编码是Universal Multiple-Octet Coded Character Set 通用多八位编码字符集的简称,是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。

但即使扩展到256个符号也不够用,比如汉字据统计有10万个以上,而且同一个数值在各国的语言中表示的却不同,比如130在法语里面é,而在希腊语里面则代表Gimel,于是UNICODE应运而生。

Unicode是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode 标准始终使用十六进制数字,而且在书写时在前面加上前缀“U+”,例如字母“A”的编码为 004116 和字符“?”的编码为 20AC16。所以“A”的编码书写为“U+0041”。但Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

3.1 Python程序文件的编码

例如,Python2自带的IDE,当创建了一个文件保存的时候提示:

图片 9

Paste_Image.png

这是因为Python2编辑器默认的编码是ASCII,它是无法识别中文的,所以会弹出这样的提示。这也是我们在大多数情况下写Python2程序的时候习惯在程序的第一行加上:#coding: utf-8。

5.UTF8编码

事实证明,对可以用ASCII表示的字符使用UNICODE并不高效,因为UNICODE比ASCII占用大一倍的空间,而对ASCII来说高字节的0对 他毫无用处。为了解决这个问题,就出现了一些中间格式的字符集,他们被称为通用转换格式,即UTF(Universal Transformation Format)。目前存在的UTF格式有:UTF-7, UTF-7.5, UTF-8, UTF-16, 以及 UTF-32。

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第 一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传 送文字的应用中,优先采用的编码。

UTF-8用1~4个字节对Unicode进行编码。从Unicode到UTF-8的编码方式如下:

000000 - 00007F║0xxxxxxx
000080 - 0007FF║110xxxxx 10xxxxxx
000800 - 00FFFF║1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF║11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同; 
带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要二个字节编码(Unicode范围由); 
其他基本多文种平面(BMP)中的字符(这包含了大部分常用字)使用三个字节编码; 
其他极少使用的Unicode 辅助平面的字符使用四字节编码; 
UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。

UTF-8解析算法:

如果字节(Byte)的第一位为0,则B为ASCII码,并且Byte独立的表示一个字符; 
如果字节(Byte)的第一位为1,第二位为0,则Byte为一个非ASCII字符(该字符由多个字节表示)中的一个字节,并且不为字符的第一个字节编码; 
如果字节(Byte)的前两位为1,第三位为0,则Byte为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由两个字节表示; 
如果字节(Byte)的前三位为1,第四位为0,则Byte为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由三个字节表示; 
如果字节(Byte)的前四位为1,第五位为0,则Byte为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由四个字节表示。

3.2 Python程序运行时环境(IDE)的编码

执行下面一段程序

#coding: utf-8
from selenium import webdriver

driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
# 返回百度页面底部备案信息
text = driver.find_element_by_id("cp").text
print(text)
driver.close()

在windows cmd下执行:

图片 10

Paste_Image.png

我们要获取的信息是:©2015 Baidu 使用百度前必读 意见反馈 京ICP证030173号
Windows cmd用的是cp936,也就是中文的GB2312,在GBK的字符集里没有"©",这就导致通过GBK解析的时候出现编码问题。
那假设,我还就想在cmd下执行这个python程序了,那么可以去修改cmd的默认编码类型为utf-8,对应的编码为CHCP 65001(utf-8)。在cmd下输入:chcp 65001命令回车。

图片 11

Paste_Image.png

然后,修改cmd的字体为"Lucida Console",再来执行程序就可以被正确输出了。

图片 12

Paste_Image.png

6.ANSI与ASCII编码区别

  • 字面上差异:ANSI指美国国家标准协会,ASCII指美国信息互换标准代码
  • ANSI可以说是ASCII的扩展(为了支持非拉丁语系的语言)一方面,他将ascii码扩展到8bits,增加了0x80-0xff共128个字符。另一方面,在cjk(chinese japanese korean)系统中,ANSI在不同语言中有不同的具体标准,在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。
  • ansi编码,就是一种未经国际标准化的编码(也没办法标准化,因为扩展部分的内码存在交集);而Unicode为国际化的编码。

3.3 Python程序读取外部文件、网页的编码

N/A

7.GB2312

3.4 chardet模块

chardet是一个非常优秀的编码识别模块。
通过pip安装
>pip install chardet

>>> from chardet import detect
>>> a = "中文"
>>> detect(a)
{'confidence': 0.682639754276994, 'encoding': 'KOI8-R'}
>>> 

7.1 名称及制定时间

《信息交换用汉字编码字符集》是由中国国家标准总局1980年发布,标准号是GB 2312—1980,所以简称为GB2312。

3.5 Python的字符串,Python对Unicode的支持

因为Python的诞生比Unicode标准发布的时间还要早,所以最早的Python只支持ASCII编码,普通的字符串'ABC'在Python内部都是ASCII编码的。Python提供了ord()和chr()函数,可以把字母和对应的数字相互转换:

>>> ord('A')
65
>>> chr(65)
'A'
>>> 

Python在后来添加了对Unicode的支持,以Unicode表示的字符串用u'...'表示,比如:

>>> print u'中文'
中文
>>> u'中'
u'u4e2d'
>>> len(u'中')
1
>>> 

写u'中'和u'u4e2d'是一样的,u后面是十六进制的Unicode吗。因此, u'A'和u'u0041'也是一样的。
两种字符串如何相互转换?字符串'xxx'虽然是ASCII编码,但也可以看成是UTF-8编码,而u'xxx'则只能是Unicode编码。把u'xxx'转换为UTF-8编码的'xxx'用encode('utf-8')方法:

>>> u'ABC'.encode('utf-8')
'ABC'
>>> u'中文'.encode('utf-8')
'xe4xb8xadxe6x96x87'
>>> 

英文字符转换后表示的UTF-8的值和Unicode值相等(但占用的存储空间不同),而中文字符转换后1个Unicode字符将变为3个UTF-8字符,你看到的xe4就是其中一个字节,因为的值是228,没有对应的字母可以显示,所以以十六进制显示字节的数字。len()函数可以返回字符串的长度:

>>> len(u'ABC')
3
>>> len('ABC')
3
>>> len(u'中文')
2
>>> len('xe4xb8xadxe6x96x87')
6
>>> 

反过来,把UTF-8编码表示的字符串'xxx'转换为Unicode字符串u'xxx',用decode('utf-8')方法:

>>> 'abc'.decode('utf-8')
u'abc'
>>> 'xe4xb8xadxe6x96x87'.decode('utf-8')
u'u4e2du6587'
>>> print 'xe4xb8xadxe6x96x87'.decode('utf-8')
中文

由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按照UTF-8编码读取文件,我们通常在文件开头写上这两行:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
如果你使用Notepad++进行编辑,除了要加上# -- coding: utf-8 --外,中文字符串必须是Unicode字符串:

图片 13

Paste_Image.png

申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保Notepad++正在使用UTF-8 without BOM编码:

图片 14

Paste_Image.png

如果.py文件本身使用UTF-8编码,并且也申明了# -- coding: utf-8 --,打开命令提示符测试就可以正常显示中文:

图片 15

Paste_Image.png

7.2 编码格式

在使用GB2312的程序中,通常采用EUC储存方法,以便兼容于ASCII。浏览器编码表上的“GB2312”,通常都是指“EUC-CN”表示法。

每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节)”,第二个字节称为“低位字节”(也称“位字节”)。
“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。

例如“啊”字在大多数程序中,会以两个字节,0xB0(第一个字节) 0xA1(第二个字节)储存。区位码=区字节+位字节(与区位码对比:0xB0=0xA0+16,0xA1=0xA0+1)。

GB 2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时,GB 2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

4 实际问题

相信很多用Sublime Text来写Python2的同学都遇到过一下这个问题:在Sublime Text里用Ctrl+B运行代码print u'中文',想要打印出unicode类型的字符串时,会出现以下报错,UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)。
分析:
Python在向控制台(console)print的时候,因为控制台只能看得懂由bytes(字节序列)组成的字符串,而Python中"unicode"对象存储的是code points(码点),因此Python需要将输出中的“unicode”对象用编码转换为储存bytes(字节序列)的"str"对象后,才能进行输出。
而在报错里看到UnicodeEncodeError, 那就说明 Python 在将 unicode 转换为 str 时使用了错误的编码。而为什么是 'ascii' 编码呢?那是因为 Python 2 的默认编码就是 ASCII,可以通过以下命令来查看 Python 的默认编码。

>>> import sys
>>> print sys.getdefaultencoding()
ascii

所以此时在Sublime Text里运行print u'中文',实际上等于是运行了,

print u'中文'.encode('ascii')

ASCII编码无法对unicode的中文进行编码,因此就报错了。
那为什么同样的代码 print u'中文' 在 Mac 的终端里却能正常输出中文,难道是因为终端下的 Python 2 的默认编码不是 ASCII?非也,在终端下运行 sys.getdefaultencoding() 结果一样是 ascii。那同样是 ascii 为什么会有不同的结果?难倒这里 Python 用了另外一个编码来转换?
是的,其实 Python 在 print unicode 时真正涉及到的是另一组编码:stdin/stdout/stderr 的编码,也就是标准输入、标准输出和标准错误输出的编码。可以通过以下命令来查看,这里是在我的终端下运行的结果:

>>> import sys
>>> print sys.stdin.encoding
UTF-8
>>> print sys.stdout.encoding
UTF-8
>>> print sys.stderr.encoding
UTF-8

在正常情况下,Python2在print unicode时用来转换的编码并不是Python的默认编码sys.getdefaultencoding(),而是sys.stdout.encoding所设的编码。
因为在我的终端下 Python 的 sys.stdout.encoding 编码是 UTF-8,所以在终端里运行 print u'中文' 时,实际上是等于运行了:

print u'中文'.encode('UTF-8')

编码正确,运行正常,因此没有报错。
在类 UNIX 系统下,Python 应该是通过环境变量 LC_CTYPE 来判断 stdin/stdout/stderr 的编码的。因此一般只要将 shell 的 LANG 环境变量设置对为 _.UTF-8 后,应该就能在终端里直接 print 出 unicode 类型的字符串了,而不需要在 print 时手动加上 .encode('utf-8') 进行编码了。
但在 Sublime Text 里事情就没那么美好了。在 Sublime Text 里运行查看 stdout 编码的命令,发现:

import sys
print sys.stdout.encoding
-----------------------------
None
[Finished in 0.1s]

结果甚至不是 'ascii' 而是 None。可能是因为 Sublime Text 的 Build System 是用 subprocess.Popen 来运行 Python 的,导致 Python 无法判断出正确的 stdin/stdout/stderr 编码,于是都变成 None 了。

这种情况也发生在输出的目标是管道的情况下:

$ python -c 'import sys; print sys.stdout.encoding' | tee /tmp/foo.txt
None

那么在这种 sys.stdout.encoding 为 None 情况下的 print unicode 怎么办呢?答案就是 Python 只能很无奈地使用 sys.getdefaultencoding() 的默认编码 ascii 来对 unicode 进行转换了。这样就出现了本文开头所说的那个 UnicodeEncodeError 问题。

7.3 特点

GB 2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。
对于人名、古汉语等方面出现的罕用字,GB 2312不能处理,这导致了后来GBK及GB 18030汉字字符集的出现。

总结

总结一下Python 2向控制台print输出时的流程:

  1. Python启动时,当它发现当前的输出是连接到控制台的时候,它会根据一些环境变量,例如环境变量LC_CTYPE,来设法判断出sys.stdin/stdout/stderr.encoding编码值。
  2. 当Python无法判断出所需的编码时,它会将sys.stdin/stdout/stderr.encoding的值设置为None。
  3. print时判断字符串是否是unicode类型。
  4. 如果是的话,并且sys.stdout.encoding不为None时,就使用sys.stdout.encoding编码对unicode编码成str后输出。
  5. 如果sys.stdout.encoding为None的话,就使用sys.getdefaultencoding()默认编码来对unicode进行转换成str后输出。
if sys.stdout.encoding:
    print unicode.encode(sys.stdout.encoding)
else:
    print unicode.encode(sys.getdefaultencoding())

8.GBK

解决方法

8.1 名称及制定时间

GBK全称《汉字内码扩展规范》,全国信息技术标准化技术委员会1995年12月1日制订。

解决方法1:

先说最不正确的解决方法: 在文件头部加上,

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

这种方法通过dirty hack的方式在Python刚刚启动时更改了Python的默认编码为utf-8。此后:

>>> print sys.getdefaultencoding()
utf-8

但就本文所讨论的问题来说,这个方法并不是真正地直接解决了问题。就如上所说,Python只是在sys.stdout.encoding为None时,才会使用默认编码来转换需要print的unicode字符串。那万一在sys.stdout.encoding存在,但为ascii的情况下呢?这样即使更改了Python的默认编码,同样还是会出现UnicodeEncodeError报错。所以对本问题来说,这个方法治标不治本。
除此之外,很多人都用这个方法来解决Python 2下遇到的其它各种各样的编码问题。但实际上很多大牛都不推荐用这个方法来解决Python 2的编码问题,这里引用下 StackOverflow 相关回答 里的一句话:

the use of sys.setdefaultencoding() has always been discouraged

为什么这个方法不被推荐呢?我们来看下Python文档里对这个function是怎么说的:

This function is only intended to be used by the site module implementation and, where needed, by sitecustomize. Once used by the site module, it is removed from the sys module's namespace.

可以看到这个方法原本就不是面向用户的方法,并没有打算让用户用这个方法来更改Python 2的默认编码。
那为什么不建议我们更改 Python 的默认编码呢?这里引用 Python 核心开发者、Python Unicode 支持的设计者和实现者: Marc-André Lemburg,他在一个邮件列表上的回复:

The only supported default encodings in Python are:

Python 2.x: ASCII

Python 3.x: UTF-8

If you change these, you are on your own and strange things will
start to happen. The default encoding does not only affect
the translation between Python and the outside world, but also
all internal conversions between 8-bit strings and Unicode.

Hacks like what's happening in the pango module (setting the
default encoding to 'utf-8' by reloading the site module in
order to get the sys.setdefaultencoding() API back) are just
downright wrong and will cause serious problems since Unicode
objects cache their default encoded representation.

Please don't enable the use of a locale based default encoding.

If all you want to achieve is getting the encodings of
stdout and stdin correctly setup for pipes, you should
instead change the .encoding attribute of those (only).

--

Marc-Andre Lemburg

eGenix.com

从此可见,Python 2 唯一支持的内部编码只有 ASCII,更改其默认编码为其它编码可能会导致各种各样奇怪的问题。在这里他也说了使用 sys.setdefaultencoding() 的方法是彻彻底底的错误,正确的方法应该是更改 stdout 和 stdin 的编码。
所以这个方法是最不正确的填坑方法,请大家慎用。

8.2 编码格式

GBK编码,是在GB2312-80标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年10月制定, 1995年12月正式发布,目前中文版的WIN95、WIN98、WINDOWS NT以及WINDOWS 2000、WINDOWS XP、WIN 7等都支持GBK编码方案。

GBK 是 GB2312的扩展 ,除了兼容GB2312外,它还能显示繁体中文,还有日文的假名。

解决方法 2:

然后说说应当是姿势最正确的,也是大家都懂的方法:
在 print的时候显式地用正确的编码来对 unicode 类型的字符串进行encode('正确的编码')为 str 后, 再进行输出。而在 print的时候,这个正确的编码一般就是 sys.stdout.encoding的值。但也正如上述所说,这个值并不是一直是可靠的,因此需要根据所使用的平台和控制台环境来判断出这个正确的编码。而在 Mac 下这个正确的编码一般都是 utf-8,因此若不考虑跨环境的话,可以无脑地一直用 encode('utf-8') 和 decode('utf-8') 来进行输入输出转换。
在我的经验中,这个策略也是解决 Python 2 其它 unicode 相关编码问题的最佳方法。在 PyCon 2012 的一个演讲中(关于 Python Unicode 问题很好的一个演讲,这里有演讲稿的中文翻译版),对这个方法有一个很形象的比喻:

图片 16

Paste_Image.png

因为在程序中进进出出的只有存储 bytes(字节序列)的 str。因此最好的策略是将输入的 bytes 马上解码成 unicode,而在程序内部中均使用 unicode,而当在进行输出的时候,尽早将之编码成 bytes。
也就是要形成一个 Unicode 三明治(如图), bytes 在外,Unicode 在内。在边界的地方尽早进行 decode 和 encode。不要在内部混用 str 和 unicode,尽可能地让程序处理的字符串都为Unicode。

8.3 说明

GB2312是中国规定的汉字编码,也可以说是简体中文的字符集编码;GBK 是 GB2312的扩展 ,除了兼容GB2312外,它还能显示繁体中文,还有日文的假名。

解决方法 3:

虽然解决方法2是最正确的方式,但是有时候在Sublime Text里调试些小脚本,实在是懒得在每个print语句后面写一个尾巴.encode('utf-8')。那么有没有办法能让Sublime Text像在终端里一样直接就能print u'中文'呢?也就是说能不能解决sys.stdin/stdout/stderr.encoding为None的情况呢?
答案肯定是有的,一种方法是用类似更改默认编码的方法一样,用 dirty hack 的方式在 Python 代码中去显式地更改sys.stdin/stdout/stderr.encoding 的值。一样是不推荐,我也没尝试过,在这里就不详说了。
另一种方法则是通过设置 PYTHONIOENCODING 环境变量来强制要求 Python 设置 stdin/stdout/stderr 的编码值为我们想要的,这是一个相对比较干净的解决方法。见文档:

PYTHONIOENCODING

Overrides the encoding used for stdin/stdout/stderr, in the syntax encodingname:errorhandler. The :errorhandler part is optional and has the same meaning as in str.encode().

New in version 2.6.

在 Mac 下对全局 GUI 程序设置环境变量的方法是:使用 launchctl setenv <<key> <value>, ...>命令对所有 launchd 启动的未来子进程设置环境变量。
在这里顺便科普下,为什么对所有 launchd 启动的未来子进程设置环境变量可以使得对 Mac 下所有 GUI 程序生效。这是因为 launchd 是 OS X 系统启动后运行的第一个非内核进程。我们可以在 activity monitor(活动监视器)里看到,它的 pid 是很帅气的 1。而之后所有的进程都将是它的子进程。另外还可以通过 launchd 在 Mac 下实现类 crontab 的功能。
launchctl setenv命令设置的全局环境变量会在电脑重启后失效,因此就需要通过上面说的 launchd 的开机启动任务的功能来在重启后再设置一遍环境变量,其配置方法可以参考这里。也因为这个原因,我并没有使用这个方法来设置 PYTHONIOENCODING环境变量。
而 Sublime Text 提供了一个设置 Build System 环境变量的方法,这个方法各平台的 Sublime Text 都适用。
设置 Sublime Text 的 Python Build System 环境变量的步骤如下:

  1. 将 Sublime Text 默认的 Python Build System 的配置文件Python.sublime-build(找到这个文件的最好方法是安装插件 PackageResourceViewer)复制一份到 Sublime Text 的 /Packages/User 文件夹下(在 Mac 和 Sublime Text 3 下这个路径是 ~/Library/Application Support/Sublime Text 3/Packages/User)。
  2. 打开编辑新复制来的 Python.sublime-build 文件,如下加上一行设置 PYTHONIOENCODING环境变量为 UTF-8 编码的内容,并保存:
{
    "shell_cmd": "python -u "$file"",
    "file_regex": "^[ ]*File "(...*?)", line ([0-9]*)",
+   "env": {"PYTHONIOENCODING": "utf8"},
    "selector": "source.python"
}

这样一来终于在这么长的文章后能在 Sublime Text 里直接运行 print u'中文',而不用再出现万恶的 UnicodeEncodeError 了。
既然都研究到这了,不妨我们试试把 PYTHONIOENCODING 设置成其它编码看看会出现什么情况,例如设置成简体中文 Windows 的默认编码 cp936:"env": {"PYTHONIOENCODING": "cp936"}

import sys
print sys.stdout.encoding
print u'你好'
----------------------------------
cp936
[Decode error - output not utf-8]
[Finished in 0.1s]

[Decode error - output not utf-8],这就是 Sublime Text 在 Windows 下可能会出现的问题。这是因为 Sublime Text 的 Build System 默认是用utf-8 编码去解读运行的输出的,而我们指定了让 Python 用 cp936 编码来生成 str 字符串进行输出,那么就会出现 Sublime Text 无法识别输出的情况了。同样在对终端 export PYTHONIOENCODING=cp936后,在终端下 print u'你好' 输出的就会是 ���这样的乱码解决办法之一就是同样在 Python.sublime-build 文件里设置 "env": {"PYTHONIOENCODING": "utf8"}来使得输出统一为 utf-8。或者是更改 Sublime Text 的 Build System 所接受的输出编码,将其改为一致的 cp936 编码,同样也是更改 Python.sublime-build 文件,加入一行:

{
    "shell_cmd": "python -u "$file"",
    "file_regex": "^[ ]*File "(...*?)", line ([0-9]*)",
+   "encoding": "cp936",
    "selector": "source.python"
}

那我们再试试把这两个设置同时都加到 Python.sublime-build 文件里,也就是让 Python 输出 utf8 编码的字符串,而让 Sublime Text 用 cp936 编码来解读,看看会发生什么情况?

{
    "shell_cmd": "python -u "$file"",
    "file_regex": "^[ ]*File "(...*?)", line ([0-9]*)",
+   "env": {"PYTHONIOENCODING": "utf8"},
+   "encoding": "cp936",
    "selector": "source.python"
}

print u'你好'
----------------------
浣犲ソ
[Finished in 0.1s]

笑,居然不是 [Decode error - output not cp936],而是这么喜感的 “浣犲ソ”!
这是因为 “你好” 的 utf-8 编码刚好和 “浣犲ソ” 的 cp936 编码重合了,都是 'xe6xb5xa3xe7x8axb2xe3x82xbd',所以使用 cp936 编码去解读的 Sublime Text 就认为这段字符串就是 “浣犲ソ” 而显示了出来。

>>> print repr('浣犲ソ')  # cp936 编码
'xe6xb5xa3xe7x8axb2xe3x82xbd'
>>> print repr(u'你好'.encode('utf-8'))  # utf-8 编码
'xe6xb5xa3xe7x8axb2xe3x82xbd'

9.Python idle默认编码方式

如下操作均在Python2.7 idle中实验

图片 17

图片 18

说明:1.'a'的编码仍然是'a',‘中'编码为0xd6和0xd0两个字节( 而且是0x80~0xFF 范围内),说明编码方式为扩展的ASCII(ANSI)

参考

后续继续更新

 

  

5 QA

四、Python模块之codecs 

python对多国语言的处理是支持的很好的,它可以处理现在任意编码的字符,这里深入的研究一下python对多种不同语言的处理。
有一点需要清楚的是,当python要做编码转换的时候,会借助于内部的编码,转换过程请参考上文第一张图片。

Unicode编码有两种,一种是UCS-2,用两个字节编码,共65536个码位;另一种是UCS-4,用4个字节编码,共2147483648个码位。

python都是支持的,这个是在编译时通过--enable- unicode=ucs2或--enable-unicode=ucs4来指定的。那么我们自己默认安装的python有的什么编码怎么来确定呢?有一个 办法,就是通过sys.maxunicode的值来判断:

1
2
import  sys
print  sys.maxunicode

如果输出的值为65535,那么就是UCS-2,如果输出是1114111就是UCS-4编码。  

我们要认识到一点:当一个字符串转换为内部编码后,它就不是str类型了!它是unicode类型

1
2
3
4
a  =   " 风卷残云 "
print  type(a)
b  =  unicode(a,‘gb2312')
print  type(b)

运行结果:

1
2
<type 'str'>
<type 'unicode'>

这个时候b可以方便的任意转换为其他编码,比如转换为utf-8

1
2
c  =  b.encode(’utf8')
print  c

好了,该说说codecs模块了,它和我上面说的概念是密切相关的。codecs专门用作编码转换,当然,其实通过它的接口是可以扩展到其他关于代码方面的转换的,这个东西这里不涉及。

  

  

  

参考文档: 

 

 

1.1 请教个问题,文件头部定义的编码#--coding: utf-8 -- 和sys.setdefaultencoding()定义的编码是一回事吗?

#--coding:utf-8--指明'当前文件'的encoding。sys.getdefaultencoding()是python默认把字符转换为unicode的encoding。举个例子,假如你的某个源代码文件里有韩文,并且由于不知什么原因,源代码必须用韩文euc-kr。这时你得在源文件开头声明#coding: euc-kr。
然后这个文件里有这么一段代码:

a = b'hello'
b = u"#*%韩%#@#%文!@#$字$%符" 
c = a + b

虚拟机在执行'c = a + b'这句的时候,发现a是一个byte string,b是一个unicode string。它就会用default encoding先去decode一下a,再去和b进行拼接。所以实际执行的是'c = a.decode(DEFAULT_ENCODING) + b'。
其中的DEFAULT_ENCODING在python2下默认是'ascii',可以用reload(sys): sys.setdefaultencoding('utf-8')改成'utf-8',在python3下默认就是'utf-8'。

参考:

  1. http://www.cnblogs.com/fnng/p/5008884.html
  2. http://www.liaoxuefeng.com
  3. https://www.v2ex.com/t/163786
上一篇:JavaScript框架是什么,怎样才能叫做框架 下一篇:没有了
返回顶部