您的位置:首页 > 编程语言 > Python开发

Python建议:考虑兼容性,尽可能使用Unicode

2016-12-06 15:52 836 查看
Python内建字符串有两种类型:str和Unicode。它们的共同祖先是basestring。Unicode是Python2.0中引入的一种新的数据类型。所有Unicode字符串都是Unicode类的实例。

# coding:utf-8
strUnicode = u'unicode 字符串' # 前面加u表示Unicode
raw = '%r' % strUnicode
# 原生输出,默认print打印的时候会自动进行编码。
print(raw)
print("让print函数自动进行编码:")
print(strUnicode)
print("打印字符串的类:")
print(type(strUnicode))
print("打印字符串的类的父类:")
print(type(strUnicode).__base__)
print("打印字符串的类的父类的父类:")
print(type(strUnicode).__base__.__base__)
print("打印字符串的类的父类的父类的父类:")
print(type(strUnicode).__base__.__base__.__base__)


为了统一不同编码,Unicode产生了,它也被称谓万国码。Unicode编码系统可以分成编码方式和实现方式两个层次。在编码方式上,分为UCS-2和UCS-4两种方式。UCS-2使用两个字节编码。而UCS-4使用4个字节编码。目前实际应用的统一码对应于UCS-2使用16bit的编码空间。一个字符的Unicode编码是确定的,但实际传输过程,由于系统平台不同以及处于节约空间的目的,实现方式有所差异。Unicode的实现方式被称为UTF(Unicode Transformation Format),包括UTF-7,UTF-8,UTF-16,UTF-32,其中最常用的为UTF-8。特点是对不同范围的字符使用不同长度的编码方式。其中0x00到0x7F的字符的UTF-8编码与ASCII编码完全相同(因此以ASCII来输出UTF-8时,英文输出没问题,如下表所示,读取以0开头的字节都等价于ASCII编码)。

注意:ASICII 美国信息交换标准编码(American Standard Code for Information Interchange),是用于表示字符的8位编码(7位再加上校验位)。正确的拼法应该是ASCII,而不是ASICII。这是某些人笔误的结果。世界上没有ASICII这种编码,只有ASCII。

Unicode和UTF-8编码的互转方式

Unicode编码(十六进制) UTF-8字节流(二进制)
U-00000000 - U-0000007F 0xxxxxxx
U-00000080 - U-000007FF 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
Unicode经过多年发展,已经成为业界标准。

Python中文处理会遇到以下几个问题:

例子1

在Windows控制台运行以下代码,文本使用UTF-8编码,文本test的内容如下所示:

python 中文测试 python
python 中文测试 python
python 中文测试 python


代码如下所示:
import locale
filehandle = open(r'E:\test.txt', 'r')
print(filehandle.readline())
print(filehandle.readline().decode('utf-8'))
print("The locale is:")
print(locale.getdefaultlocale())
print(filehandle.readline().decode('utf-8').encode('gbk'))
filehandle.close()


此时输出结果如下所示:



对于第一句输出,由于文本是UTF-8编码,而控制台使用的编码为GBK编码,故此时输出乱码。



对于第二句输出,由于对文本使用了UTF-8解码,Python在调用print函数时自动进行编码(和环境变量有关)为GBK后输出。使用print(locale.getdefaultlocale())可以查看当前控制台的编码方式。CP936几乎就是GBK,IBM在发明Code Page的时候将GBK放在第936页,所以叫CP936。但其实两者稍微有区别,这里不做展开。

cp936和GBK还是有些差别的。淘宝的技术团队发现他们的MYSQL数据库在GBK字符集下无法插入欧元符号。然后就打了一个补丁。 其实也不能说MYSQL的代码有问题,GBK中的确没有0x80这个字符。但是cp936中有。可是cp936又不是标准,因为即便是微软内部,不同的windows版本的CP936范围都不一样。

编码的问题害死很多人啊http://rdc.taobao.com/blog/cs/?p=679

以下是常见的编码参数:



解码和编码时可以选择码和出错时的处理方式,错误处理方式一般有三种:

1) strict,出错时抛出UnicodeDecodeError异常。

2) ignore,出错时忽略不可转换字符。

3) replace,出错时使用?代替不可转换字符。

以下是解码和编码函数。

def decode(self, encoding=None, errors=None): # real signature unknown; restored from __doc__
"""
S.decode([encoding[,errors]]) -> string or unicode

Decodes S using the codec registered for encoding. encoding defaults
to the default encoding. errors may be given to set a different error
handling scheme. Default is 'strict' meaning that encoding errors raise
a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'
as well as any other name registered with codecs.register_error that is
able to handle UnicodeDecodeErrors.
"""
return ""

def encode(self, encoding=None, errors=None): # real signature unknown; restored from __doc__
"""
S.encode([encoding[,errors]]) -> string or unicode

Encodes S using the codec registered for encoding. encoding defaults
to the default encoding. errors may be given to set a different error
handling scheme. Default is 'strict' meaning that encoding errors raise
a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
'xmlcharrefreplace' as well as any other name registered with
codecs.register_error that can handle UnicodeEncodeErrors.
"""
return ""


对于A/B两种编码方式,两者转换的示意图如下所示:



BOM

实际上UTF-8编码分为带BOM和不带BOM两种:



如果文本是UTF-8带BOM的话,会有其他乱码出现,或者有报错信息。





使用codecs模块可以很方便地处理带BOM的UTF-8文件。

# coding:utf-8
import locale
import codecs
filehandle = open(r'E:\test.txt', 'r')
firstline = filehandle.readline()
if firstline[:3] == codecs.BOM_UTF8:
print(firstline[3:].decode('utf-8'))
print("The locale is:")
print(locale.getdefaultlocale())
print(filehandle.readline().decode('utf-8').encode('gbk'))
filehandle.close()


以下是关于BOM的相关知识:

Unicode存储有字节序问题,例如“汉”一字,的Unicode编码0x6C49。存储时,6C写在前面,则称谓Big Endian,将49写在前面则称为Little Endian。UTF-8一般不需要BOM来表标明字节序,但还是可以用BOM标明。字符Zero Width No-Break Space的UTF-8编码是ef bb bf。所以如果接受者接收到ef bb bf开头的字节流,就知道是UTF-8编码吧。

例子2

s = 'python 中文测试'
print(s)


由于Python默认编码为ASCII编码,代码中包含中文将会报错。如果需要使用中文,需要指定编码方式。



编码方式的声明有三种格式,可以用正则表达式来总结这三种编码方式 coding[:=]\s*([-\w.]+),符合这个正则表达式的都是有效的,比如

#哈哈哈coding: utf-8 哈哈哈
#fuckingcoding: utf-8
#fuckingcoding= utf-8


但下面是无效的,因为:和=前面多了空格。

#哈哈哈coding : utf-8 哈哈哈
#fuckingcoding : utf-8
#fuckingcoding = utf-8


以下是三种可选的声明方式

第一种声明方式:
#coding= <encoding name>
#coding= utf-8
第二种声明方式:
#!/usr/bin/python
# -*- coding:<encoding name> -*-
第三种声明方式:
#!/usr/bin/python
#vim: set fileencoding=<encoding name>


加上编码声明后,示例二可以正常输出。

例子3

如下所示,当str字符串和Unicode字符串连接时,会产生以下报错信息。由于先使用了ASCII对字符串进行解码再编码成UTF-8,ASIC无法正常识别中文编码,故产生报错。

# coding= utf-8
s = "中文测试"+u"Chinese Test"
print(s)




解决以上问题,有三种思路。

1)使用正确的解码方式(以python文本的编码方式作为解码)。

# coding= utf-8
s = "中文测试".decode('utf-8')+u"Chinese Test"
print(s)




2)对Unicode字符串进行编码

# coding= utf-8
s = "中文测试"+u"Chinese Test".encode('utf-8')
print(s)


3)Python2.6之后,可以通过from future import unicode_literals自动转义str字符串为Unicode字符串。

# coding= utf-8
from __future__ import unicode_literals
s = "中文测试"+u"Chinese Test".encode('utf-8')
print(s)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: