当前位置: 首页 > 编程日记 > 正文

Python字符编码详解

Python字符编码详解

转自http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html

Python字符编码详解

本文简单介绍了各种常用的字符编码的特点,并介绍了在python2.x中如何与编码问题作战 :) 
请注意本文关于Python的内容仅适用于2.x,3.x中str和unicode有翻天覆地的变化,请查阅其他相关文档。 
尊重作者的劳动,转载请注明作者及原文地址 >.<

1. 字符编码简介

1.1. ASCII

ASCII(American Standard Code for Information Interchange),是一种单字节的编码。计算机世界里一开始只有英文,而单字节可以表示256个不同的字符,可以表示所有的英文字符和许多的控制符号。不过ASCII只用到了其中的一半(\x80以下),这也是MBCS得以实现的基础。

1.2. MBCS

然而计算机世界里很快就有了其他语言,单字节的ASCII已无法满足需求。后来每个语言就制定了一套自己的编码,由于单字节能表示的字符太少,而且同时也需要与ASCII编码保持兼容,所以这些编码纷纷使用了多字节来表示字符,如GBxxx、BIGxxx等等,他们的规则是,如果第一个字节是\x80以下,则仍然表示ASCII字符;而如果是\x80以上,则跟下一个字节一起(共两个字节)表示一个字符,然后跳过下一个字节,继续往下判断。

这里,IBM发明了一个叫Code Page的概念,将这些编码都收入囊中并分配页码,GBK是第936页,也就是CP936。所以,也可以使用CP936表示GBK。

MBCS(Multi-Byte Character Set)是这些编码的统称。目前为止大家都是用了双字节,所以有时候也叫做DBCS(Double-Byte Character Set)。必须明确的是,MBCS并不是某一种特定的编码,Windows里根据你设定的区域不同,MBCS指代不同的编码,而Linux里无法使用MBCS作为编码。在Windows中你看不到MBCS这几个字符,因为微软为了更加洋气,使用了ANSI来吓唬人,记事本的另存为对话框里编码ANSI就是MBCS。同时,在简体中文Windows默认的区域设定里,指代GBK。

1.3. Unicode

后来,有人开始觉得太多编码导致世界变得过于复杂了,让人脑袋疼,于是大家坐在一起拍脑袋想出来一个方法:所有语言的字符都用同一种字符集来表示,这就是Unicode。

最初的Unicode标准UCS-2使用两个字节表示一个字符,所以你常常可以听到Unicode使用两个字节表示一个字符的说法。但过了不久有人觉得256*256太少了,还是不够用,于是出现了UCS-4标准,它使用4个字节表示一个字符,不过我们用的最多的仍然是UCS-2。

UCS(Unicode Character Set)还仅仅是字符对应码位的一张表而已,比如"汉"这个字的码位是6C49。字符具体如何传输和储存则是由UTF(UCS Transformation Format)来负责。

一开始这事很简单,直接使用UCS的码位来保存,这就是UTF-16,比如,"汉"直接使用\x6C\x49保存(UTF-16-BE),或是倒过来使用\x49\x6C保存(UTF-16-LE)。但用着用着美国人觉得自己吃了大亏,以前英文字母只需要一个字节就能保存了,现在大锅饭一吃变成了两个字节,空间消耗大了一倍……于是UTF-8横空出世。

UTF-8是一种很别扭的编码,具体表现在他是变长的,并且兼容ASCII,ASCII字符使用1字节表示。然而这里省了的必定是从别的地方抠出来的,你肯定也听说过UTF-8里中文字符使用3个字节来保存吧?4个字节保存的字符更是在泪奔……(具体UCS-2是怎么变成UTF-8的请自行搜索)

另外值得一提的是BOM(Byte Order Mark)。我们在储存文件时,文件使用的编码并没有保存,打开时则需要我们记住原先保存时使用的编码并使用这个编码打开,这样一来就产生了许多麻烦。(你可能想说记事本打开文件时并没有让选编码?不妨先打开记事本再使用文件 -> 打开看看)而UTF则引入了BOM来表示自身编码,如果一开始读入的几个字节是其中之一,则代表接下来要读取的文字使用的编码是相应的编码:

BOM_UTF8 '\xef\xbb\xbf' 
BOM_UTF16_LE '\xff\xfe' 
BOM_UTF16_BE '\xfe\xff'

并不是所有的编辑器都会写入BOM,但即使没有BOM,Unicode还是可以读取的,只是像MBCS的编码一样,需要另行指定具体的编码,否则解码将会失败。

你可能听说过UTF-8不需要BOM,这种说法是不对的,只是绝大多数编辑器在没有BOM时都是以UTF-8作为默认编码读取。即使是保存时默认使用ANSI(MBCS)的记事本,在读取文件时也是先使用UTF-8测试编码,如果可以成功解码,则使用UTF-8解码。记事本这个别扭的做法造成了一个BUG:如果你新建文本文件并输入"姹塧"然后使用ANSI(MBCS)保存,再打开就会变成"汉a",你不妨试试 :)

2. Python2.x中的编码问题

2.1. str和unicode

str和unicode都是basestring的子类。严格意义上说,str其实是字节串,它是unicode经过编码后的字节组成的序列。对UTF-8编码的str'汉'使用len()函数时,结果是3,因为实际上,UTF-8编码的'汉' == '\xE6\xB1\x89'。

unicode才是真正意义上的字符串,对字节串str使用正确的字符编码进行解码后获得,并且len(u'汉') == 1。

再来看看encode()和decode()两个basestring的实例方法,理解了str和unicode的区别后,这两个方法就不会再混淆了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
# coding: UTF-8
u = u'汉'
print repr(u) # u'\u6c49'
s = u.encode('UTF-8')
print repr(s) # '\xe6\xb1\x89'
u2 = s.decode('UTF-8')
print repr(u2) # u'\u6c49'
# 对unicode进行解码是错误的
# s2 = u.decode('UTF-8')
# 同样,对str进行编码也是错误的
# u2 = s.encode('UTF-8')

需要注意的是,虽然对str调用encode()方法是错误的,但实际上Python不会抛出异常,而是返回另外一个相同内容但不同id的str;对unicode调用decode()方法也是这样。很不理解为什么不把encode()和decode()分别放在unicode和str中而是都放在basestring中,但既然已经这样了,我们就小心避免犯错吧。

2.2. 字符编码声明

源代码文件中,如果有用到非ASCII字符,则需要在文件头部进行字符编码的声明,如下:

?
1
#-*- coding: UTF-8 -*-

实际上Python只检查#、coding和编码字符串,其他的字符都是为了美观加上的。另外,Python中可用的字符编码有很多,并且还有许多别名,还不区分大小写,比如UTF-8可以写成u8。参见http://docs.python.org/library/codecs.html#standard-encodings。

另外需要注意的是声明的编码必须与文件实际保存时用的编码一致,否则很大几率会出现代码解析异常。现在的IDE一般会自动处理这种情况,改变声明后同时换成声明的编码保存,但文本编辑器控们需要小心 :)

2.3. 读写文件

内置的open()方法打开文件时,read()读取的是str,读取后需要使用正确的编码格式进行decode()。write()写入时,如果参数是unicode,则需要使用你希望写入的编码进行encode(),如果是其他编码格式的str,则需要先用该str的编码进行decode(),转成unicode后再使用写入的编码进行encode()。如果直接将unicode作为参数传入write()方法,Python将先使用源代码文件声明的字符编码进行编码然后写入。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# coding: UTF-8
f = open('test.txt')
s = f.read()
f.close()
print type(s) # <type 'str'>
# 已知是GBK编码,解码成unicode
u = s.decode('GBK')
f = open('test.txt', 'w')
# 编码成UTF-8编码的str
s = u.encode('UTF-8')
f.write(s)
f.close()

另外,模块codecs提供了一个open()方法,可以指定一个编码打开文件,使用这个方法打开的文件读取返回的将是unicode。写入时,如果参数是unicode,则使用open()时指定的编码进行编码后写入;如果是str,则先根据源代码文件声明的字符编码,解码成unicode后再进行前述操作。相对内置的open()来说,这个方法比较不容易在编码上出现问题。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# coding: GBK
import codecs
f = codecs.open('test.txt', encoding='UTF-8')
u = f.read()
f.close()
print type(u) # <type 'unicode'>
f = codecs.open('test.txt', 'a', encoding='UTF-8')
# 写入unicode
f.write(u)
# 写入str,自动进行解码编码操作
# GBK编码的str
s = '汉'
print repr(s) # '\xba\xba'
# 这里会先将GBK编码的str解码为unicode再编码为UTF-8写入
f.write(s)
f.close()

2.4. 与编码相关的方法

sys/locale模块中提供了一些获取当前环境下的默认编码的方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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)
# \xba\xba是'汉'的GBK编码
# mbcs是不推荐使用的编码,这里仅作测试表明为什么不应该用
print r"'\xba\xba'.decode('mbcs'):", repr('\xba\xba'.decode('mbcs'))
#在笔者的Windows上的结果(区域设置为中文(简体, 中国))
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp936')
#locale.getpreferredencoding(): cp936
#'\xba\xba'.decode('mbcs'): u'\u6c49'

3.一些建议

3.1. 使用字符编码声明,并且同一工程中的所有源代码文件使用相同的字符编码声明。

这点是一定要做到的。

3.2. 抛弃str,全部使用unicode。

按引号前先按一下u最初做起来确实很不习惯而且经常会忘记再跑回去补,但如果这么做可以减少90%的编码问题。如果编码困扰不严重,可以不参考此条。

3.3. 使用codecs.open()替代内置的open()。

如果编码困扰不严重,可以不参考此条。

3.4. 绝对需要避免使用的字符编码:MBCS/DBCS和UTF-16。

这里说的MBCS不是指GBK什么的都不能用,而是不要使用Python里名为'MBCS'的编码,除非程序完全不移植。

Python中编码'MBCS'与'DBCS'是同义词,指当前Windows环境中MBCS指代的编码。Linux的Python实现中没有这种编码,所以一旦移植到Linux一定会出现异常!另外,只要设定的Windows系统区域不同,MBCS指代的编码也是不一样的。分别设定不同的区域运行2.4小节中的代码的结果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#中文(简体, 中国)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp936')
#locale.getpreferredencoding(): cp936
#'\xba\xba'.decode('mbcs'): u'\u6c49'
#英语(美国)
#sys.getdefaultencoding(): UTF-8
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp1252')
#locale.getpreferredencoding(): cp1252
#'\xba\xba'.decode('mbcs'): u'\xba\xba'
#德语(德国)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp1252')
#locale.getpreferredencoding(): cp1252
#'\xba\xba'.decode('mbcs'): u'\xba\xba'
#日语(日本)
#sys.getdefaultencoding(): gbk
#sys.getfilesystemencoding(): mbcs
#locale.getdefaultlocale(): ('zh_CN', 'cp932')
#locale.getpreferredencoding(): cp932
#'\xba\xba'.decode('mbcs'): u'\uff7a\uff7a'

可见,更改区域后,使用mbcs解码得到了不正确的结果,所以,当我们需要使用'GBK'时,应该直接写'GBK',不要写成'MBCS'。

UTF-16同理,虽然绝大多数操作系统中'UTF-16'是'UTF-16-LE'的同义词,但直接写'UTF-16-LE'只是多写3个字符而已,而万一某个操作系统中'UTF-16'变成了'UTF-16-BE'的同义词,就会有错误的结果。实际上,UTF-16用的相当少,但用到的时候还是需要注意。

--END--

posted on 2012-03-29 11:01 linyawen 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/linyawen/archive/2012/03/29/2422960.html

相关文章:

《编写有效用例》读书笔记1

第一章 引言 本章主要介绍用例是什么样子的&#xff0c;并描述为什么不同的项目组需要采用不同 的用例编写风格以及在什么地方使用用例有利于做需求收集工作&#xff0c;也让我们了解 在编写用例之前&#xff0c;需要做哪些准备工作。 用例是代表系统中各个项目相关人员之间就系…

顺序结构,判断结构 if,switch

1&#xff1a;顺序结构&#xff1a;从上往下执行程序代码&#xff0c;为顺序结构 ---------------------------------------------------------------------- 2&#xff1a;判断结构&#xff1a; if 如果 判断是两个选择一个&#xff0c;要么对要么错 if中的条件表达式 返回…

php转换文字Unicode,php实现将中文转为unicode的方法

相关函数说明&#xff1a;iconv命令是用来转换文件的编码方式的&#xff0c;比如它可以将UTF8编码的转换成GB18030的编码&#xff0c;反过来也行。str_split() 函数把字符串分割到数组中。bin2hex() 函数把 ASCII 字符的字符串转换为十六进制值。字符串可通过使用 pack() 函数再…

kail安装和vmtools安装

因位太费劲我直接把我做的Word文档直接上传了&#xff0c;如果想安装可以下载&#xff0c;我就不麻烦在写一遍了&#xff0c;这个教程主要给小白讲的&#xff0c;大佬请绕过 kail安装教程.docx https://pan.wps.cn/l/sm43o7f 密码&#xff1a;f43341转载于:https://www.cnblogs…

Windows计数器做性能监控(window server 2008服务器)

使用Windows计数器 一、创建数据收集器集 二、创建数据收集器 三、使用数据收集器 1、修改数据收集器的属性 2、手动启用、手动停止数据收集器集 3、计划任务 4、在性能监视器中查看 一、性能监视器 Windows 服务器操作系统提供一个名为“性能监视器”的图形工具&#xff0c;可…

使用浏览器wpf应用程序时访问数据库需要报权限错误的解决方法

在这篇wpf教程中&#xff0c;如果选用浏览器wpf应用程序模板我遇到了 访问数据库时权限不够 不能打开连接 将项目属性的安全性中设置为完全信任后即解决 转载于:https://www.cnblogs.com/langu/archive/2012/03/29/2423620.html

gprs 神奇宝典java,2016联通笔试知识点大全

答&#xff1a;呼叫转移是指用户在工作忙或手机无网络等无法用本机接听电话的情况下&#xff0c;即可将来电设置呼叫转移到另一个号码上&#xff0c;实现号码转移。注&#xff1a;呼叫转移业务目前无需月功能费&#xff0c;用户设置呼叫转移后&#xff0c;如正常接听来电则按照…

内存管理器(二)边界标识法

边界标识算法 前言 首先说明&#xff0c;我们这里的内存管理器主要是以模拟各种内存分配算法为主&#xff0c;从内存申请一片内存然后根据我们所选定的数据结构和算法&#xff0c;实现程序的堆空间的分配&#xff0c;至于内存分配详情我们会在Linux内核内存管理的简单分析中探讨…

XDOC Office Server 开源了,Office文档完美转换为PDF

百度智能云 云生态狂欢季 热门云产品1折起>>> 项目地址&#xff1a;https://gitee.com/xdoc/xoffice XDOC是一个文档自动化平台&#xff0c;提供免费的Office文档生成服务。有用户提出要PDF格式&#xff0c;便于阅读、发布。在尝试了OpenOffice、WPS、微软Office、P…

js的defer属性

js的defer属性说明&#xff1a;<script src"js.js" type"text/javascript defer"defer"/>中defer的作用 给外链的js脚本添加defer"defer" 或 defer"true",使用defer属性可以让脚本在整个页面装载完成之后再解析&#xff0c…

怎么读取java文件,Java怎么读取文件

当前位置:我的异常网 J2SE Java怎么读取文件Java怎么读取文件www.myexceptions.net 网友分享于&#xff1a;2013-12-20 浏览&#xff1a;60次Java如何读取文件?源文件如下,小弟没有学过Java,下面是一段JAVA用RSA加密字符串的程序,命令行的形式是Java PublicExample ABC…

Android环境变量的设置(详细图解版)

Android环境变量的设置&#xff08;详细图解版&#xff09; 转载于:https://www.cnblogs.com/zhujiabin/p/4875182.html

用加密货币连接业务的6种方法

如今&#xff0c;区块链技术和加密货币已经变得更加接近传统业务。在某些情况下&#xff0c;商人们能够找到一种将传 统商业与新技术相结合的有价值的模式。事实上&#xff0c;进入加密货币市场有很多选择&#xff0c;本文将讨论6种主 要的合作方式。 创建加密货币平台是为了完…

extjs grid renderer用法

renderer可以格式化该列显示的数据格式或者按照你自定义的脚本显示最终数据样子&#xff08;我目前是这么理解的&#xff09; 先看下renderer: function()里的参数 var cm new Ext.grid.ColumnModel([new Ext.grid.RowNumberer(),sm,{header:商品名称, dataIndex: name,render…

java50车架适合身高,【经验分享】身高与车架的选择

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼车架的长度&#xff1a;骑在车上&#xff0c;在正常握把时&#xff0c;眼睛、把立前端和前轮花鼓三点一线则说明车架长度正好&#xff0c;否则可通过更换不同长度的把立来调整长度。在Airborne网站上看到了度量身体个部位长度和计算…

从Android界面开发谈起(转)

原文地址&#xff1a;http://blog.csdn.net/nieweilin/article/details/5967815 这篇文章没有打算有一个很好的逻辑去介绍android的某个方面&#xff0c;全盘大致上就是我接触、了解android的ui开发后到现在的一些感想以及个人理解吧&#xff01; 全文可能会涉及到java、androi…

Android笔记之使用LocationManager获取经纬度

LocationManager.getLastKnownLocation(String provider)有可能返回null&#xff0c;概率还挺高 findViewById(R.id.llMain).setOnClickListener(new View.OnClickListener() {Overridepublic void onClick(View v) {AndPermission.with(MainActivity.this).runtime().permissi…

Java虚拟机类加载机制

虚拟机类加载机制&#xff1a;虚拟机把描述类的数据从class文件加载到内存&#xff0c;并对数据进行校验、转换解析和初始化&#xff0c;最终形成可以被虚拟机直接使用的Java类型。 Java语言里&#xff0c;类型的加载和连接过程是在程序运行期间完成的。 类的生命周期&#xff…

dw8与mysql的连接,VS2019连接mysql8.0数据库的教程图文详解

1.首先准备好vs2019以及mysql数据库&#xff0c;两者都可以去官网下载&#xff0c;我们直接描述连接过程。2.连接&#xff1a;第一步&#xff1a;打开mysql的安装目录&#xff0c;我本地的安装目录如下&#xff1a;(注意是否有include和lib文件夹)第二步&#xff1a;打开vs2019…

IOS中打开应用实现检查更新的功能

//检查更新页面- (void)Renew{ NSDictionary *infoDic [[NSBundle mainBundle]infoDictionary]; NSString *version [infoDic valueForKey:"CFBundleShortVersionString"]; NSString *ipstr [NSObject deviceIPAdress]; NSString *p…

CSS3边框背景-边框背景(-border-image)

另一个令人兴奋的新特征是边框图片。有了这项功能您可以定义一个图像被用来代替正常的边框的一个组成部分。这项功能实际上是分成了几个属性&#xff1a;边框和边框角的形象。这两个值是&#xff1a; border-image: border-top-imageborder-right-imageborder-bottom-imagebord…

Promise的实例用法

设定函数 function chiFan() {return new Promise(function(resolve, reject) {console.log("chiFan");}) }function shuiJiao() {return new Promise(function(resolve, reject) {console.log("shuiJiao");}) }function daDouDou() {return new Promise(f…

php文件夹列表,php获取文件夹下面的文件列表和文件夹列表

function getDir($dir) {$dirArray[] NULL;if (false ! ($handle opendir( $dir ))) {$i0;while ( false ! ($file readdir( $handle )) ) {//去掉"“.”、“..”以及带“.xxx”后缀的文件if ($file ! "." && $file ! ".."&&!strp…

SharePoint API测试系列——Records.BypassLocks测试

转载请注明出自天外归云的博客园&#xff1a;http://www.cnblogs.com/LanTianYou/ 对于SharePoint中已经是Record的Item&#xff0c;我们想要修改他的属性&#xff0c;这在UI界面是无法完成的&#xff1a; 这时需要通过Records.BypassLocks API来完成。设计一个tool&#xff0c…

我对自动化测试工程师招聘的建议

给以前公司招聘人员做的一个培训:/Files/killmyday/2011年PTA测试培训课件.zip 转载于:https://www.cnblogs.com/killmyday/archive/2012/04/15/2450108.html

解决请求中400的问题

具体报错&#xff1a;2019-03-19 12:01:35,097 - request - DEBUG - json_data: {timestamp: 1552968086846, status: 400, error: Bad Request, exception: org.springframework.http.converter.HttpMessageNotReadableException, message: "JSON parse error: Unrecogni…

java 中lock,java中lock获取锁的四种方法

在java接口中会存放着许多方法&#xff0c;方便线程使用时的直接调用。对于lock接口大家都不陌生&#xff0c;我们已经初步对概念进行了理解。那么在获取锁的方法上想必还不是很清楚。下面我们就lock获取锁的四种方法分别进行概念的介绍&#xff0c;然后就其中的tryLock()方法带…

RadASM的测试工程!

RadASM已经安装完毕了&#xff0c;是否可以正常工作了呢&#xff1f;我们通过创建一个工程来测试一下&#xff0c;下面就是创建这个测试工程的过程&#xff1a; 1&#xff0c; 2&#xff0c; 3&#xff0c; 4&#xff0c; 5&#xff0c; 6&#xff0c; 7&#xff0c; 8&#xf…

单片机 键盘

键盘分类&#xff1a; &#xff08;1&#xff09;编码键盘 键盘上闭合键的识别由专门的硬件编码器实现&#xff0c;并产生键编码号或键值的称为编码键盘&#xff0c;如计算 机键盘。 &#xff08;2&#xff09;非编码键盘 靠软件编程来识别的键盘称为非…

React typescript issue

多个输入框发生变化时&#xff0c;setState: this.setState({[e.target.name]: e.target.value} as componentState) 转载于:https://www.cnblogs.com/Nyan-Workflow-FC/p/10561058.html