python2.7 中unicode、str的编解码关系

本文深入探讨了Python 2.x版本中字符编码的处理机制,详细讲解了ASCII、GBK、GB2312、GB18030、Unicode及UTF-8编码的概念与转换,并提供了丰富的示例代码帮助理解。

注意:本文实验主要基于win7,Python2.7;以及Linux ,Python2.7。除非特殊说明,所有的命令都是在终端中交互式输入;如果没有强调平台,那么就是window上的结果。下面是一些默认的环境信息(其重要性后文会介绍)

windows

>>> import sys,locale

>>> sys.getdefaultencoding()

'ascii'

>>> locale.getdefaultlocale()

('zh_CN', 'cp936')

>>> sys.stdin.encoding

'cp936'

>>> sys.stdout.encoding

'cp936'

>>> sys.getfilesystemencoding()

'mbcs'


注意,上面CP936是GBK的别名,在https://docs.python.org/2/library/codecs.html#standard-encodings 可以查看。

Linux

>>> import sys,locale

>>> sys.getdefaultencoding()

'ascii'

>>> locale.getdefaultlocale()

('zh_CN', 'UTF-8')

>>> sys.stdin.encoding

'UTF-8'

>>> sys.stdout.encoding

'UTF-8'

>>> sys.getfilesystemencoding()

'UTF-8'


从字符编码说起

首先来说一说gbk gb2312 unicode utf-8这些术语,这些术语与语言无关。

计算机的世界只有0和1,因此任何字符(也就是实际的文字符号)也是由01串组成。计算机为了运算方便,都是8个bit组成一个字节(Byte),字符表达的最小单位就是字节,即一个字符占用一个或者多个字节。字符编码(character encoding)就是字集码,编码就是将字符集中的字符映射为一个唯一二进制的过程。

计算机发源于美国,使用的是英文字母(字符),所有26个字母的大小写加上数字0到10,加上符号和控制字符,总数也不多,用一个字节(8个bit)就能表示所有的字符,这就是ANSI的“Ascii”编码(American Standard Code for Information Interchange,美国信息互换标准代码)。比如,小写字母‘a’的ascii 码是01100001,换算成十进制就是97,十六进制就是0x61。计算机中,一般都是用十六进制来描述字符编码。

但是当计算机传到中国的时候,ASCII编码就行不通了,汉字这么多,一个字节肯定表示不下啊,于是有了GB 2312(中国国家标准简体中文字符集)。GB2312使用两个字节来对一个字符进行编码,其中前面的一个字节(称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,GB2312能表示几千个汉字,而且与asill吗也是兼容的。

但后来发现,GB2312还是不够用,于是进行扩展,产生了GBK(即汉字内码扩展规范), GBK同Gb2312一样,两个字节表示一个字符,但区别在于,放宽了对低字节的要求,因此能表示的范围扩大到了20000多。后来,为了容纳少数名族,以及其他汉字国家的文字,出现了GB13080。GB13080是兼容GBK与GB2312的,能容纳更多的字符,与GBK与GB2312不同的是,GB18030采用单字节、双字节和四字节三种方式对字符编码

因此,就我们关心的汉字而言,三种编码方式的表示范围是:

GB18030 》 GBK 》 GB2312

GBK是GB2312的超集,GB1803又是GBK的超集。后面也会看到,一个汉字可以用GBK表示,但不一定能被GB2312所表示

当然,世界上还有更多的语言与文字,每种文字都有自己的一套编码规则,这样一旦跨国就会出现乱码,亟待一个全球统一的解决办法。这个时候ISO(国际标准化组织)出马了,发明了”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode”。目标很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号 的编码!

unicode每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。unicode编码一定以u开头。

但是,unicode只是一个编码规范,是所有字符对应二进制的集合,而不是具体的编码规则。或者说,unicode是表现形式,而不是存储形式,就是说没用定义每个字符是如何以二进制的形式存储的。这个就跟GBK这些不一样,GBK是表里如下,表现形式即存储形式。

比如汉字“严”的unicode编码是u4e25,对应的二进制是1001110 00100101,但是当其经过网络传输或者文件存储时,是没法知道怎么解析这些二进制的,容易和其他字节混在一起。那么怎么存储unicode呢,于是出现了UTF(UCS Transfer Format),这个是具体的编码规则,即UTF的表现形式与存储格式是一样的。

因此,可以说,GBK和UTF-8是同一个层面的东西,跟unicode是另一个层面的东西,unicode飘在空中,如果要落地,需要转换成utf-8或者GBK。只不过,转换成Utf-8,大家都能懂,更懂用,而转换成GBK,只有中国人才看得懂

UTF也有不同的实现,如UTF-8, UTF-16, 这里以UTF-8为例进行讲解(下面一小节引用了阮一峰的文章)。

unicode与utf-8

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。UTF-8的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

下表总结了编码规则,字母x表示可用编码的位。

Unicode符号范围      |        UTF-8编码方式

(十六进制)           |        (二进制)

----------------------+---------------------------------------------

0000 0000-0000 007F | 0xxxxxxx

0000 0080-0000 07FF | 110xxxxx 10xxxxxx

0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx

0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx


以汉字“严”为例,演示如何实现UTF-8编码。

已知“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。

当编解码遇上Python2.x

下面使用Python语言来验证上面的理论。在这一章节中,当提到unicode,一般是指unicode type,即Python中的类型;也会提到unicode编码、unicode函数,请大家注意区别。

另外,对于编码,也有两种意思。第一个是名字,指的是字符的二进制表示,如unicode编码、gbk编码。第二个是动词,指的是从字符到二进制的映射过程。不过后文中,编码作为动词,狭义理解为从unicode类型转换成str类型的过程,解码则是相反的过程。另外强调的是,unicode类型一定是unicode编码,而str类型可能是gbk、ascii或者utf-8编码。

unicode 与 str 区别

在python2.7中,有两种“字符串”类型,分别是str 与 unicode,他们有同一个基类basestring。str是plain string,其实应该称之为字节串,因为是每一个字节换一个单位长度。而unicode就是unicode string,这才是真正的字符串,一个字符(可能多个字节)算一个单位长度。

python2.7中,unicode类型需要在文本之间加u表示。

>>> us = u'严'

>>> print type(us), len(us)

<type 'unicode'> 1

>>> s = '严'

>>> print type(s), len(s)

<type 'str'> 2

>>>


从上可以看到,第一,us、s的类型是不一样的;其二,同一个汉字,不同的类型其长度也是不一样的,对于unicode类型的实例,其长度一定是字符的个数,而对于str类型的实例,其长度是字符对应的字节数目。这里强调一下,s(s = ‘严’)的长度在不同的环境下是不一样的!后文会解释

__str__ __repr__的区别

这是python中两个magic method,很容易让新手迷糊,因为很多时候,二者的实现是一样的,但是这两个函数是用在不同的地方

_str__, 主要是用于展示,str(obj)或者print obj的时候调用,返回值一定是一个str 对象

__repr__, 是被repr(obj), 或者在终端直接打obj的时候调用

>>> us = u'严'

>>> us

u'\u4e25'

>>> print us


可以看到,不使用print返回的是一个更能反映对象本质的结果,即us是一个unicode对象(最前面的u表示,以及unicode编码是用的u),且“严”的unicode编码确实是4E25。而print调用可us.__str__,等价于print str(us),使得结果对用户更友好。那么unicode.__str__是怎么转换成str的呢,答案会在后面揭晓

unicode str utf-8关系

前面已经提到,unicode只是编码规范(只是字符与二进制的映射集合),而utf-8是具体的编码规则(不仅包含字符与二进制的映射集合,而且映射后的二进制是可以用于存储和传输的),即utf-8负责把unicode转换成可存储和传输的二进制字符串即str类型,我们称这个转换过程为编码。而从str类型到unicode类型的过程,我们称之为解码。

Python中使用decode()和encode()来进行解码和编码,以unicode类型作为中间类型。如下图所示

  decode     encode

str ---------> unicode --------->str


即str类型调用decode方法转换成unicode类型,unicode类型调用encode方法转换成str类型。for example


>>> us = u'严'

>>> ss = us.encode('utf-8')

>>> ss

'\xe4\xb8\xa5'

>>> type(ss)

<type 'str'>

>>> ss.decode('utf-8') == us

True


从上可以看出encode与decode两个函数的作用,也可以看出’严’的utf8编码是E4B8A5。

就是说我们使用unicode.encode将unicode类型转换成了str类型,在上面也提到unicode.__str__也是将unicode类型转换成str类型。二者有什么却比呢

unicode.encode 与 unicode.__str__的区别

首先看看文档

str.encode([encoding[, errors]])

  Return an encoded version of the string. Default encoding is the current default string encoding.

  

object.__str__(self)

  Called by the str() built-in function and by the print statement to compute the informal string representation of an object.


注意:str.encode 这里的str是basestring,是str类型与unicode类型的基类

可以看到encode方法是有可选的参数:encoding 和 errors,在上面的例子中encoding即为utf-8;而__str__是没有参数的,我们可以猜想,对于unicode类型,__str__函数一定也是使用了某种encoding来对unicode进行编码。

首先不禁要问,如果encode方法没有带入参数,是什么样子的:

>>> us.encode()

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

UnicodeEncodeError: 'ascii' codec can't encode character u'\u4e25' in position 0: ordinal not in range(128)


不难看出,默认使用的就是ascii码来对unicode就行编码,为什么是ascii码,其实就是系统默认编码(sys.getdefaultencoding的返回值)。ascii码显然无法表示汉字,于是抛出了异常。而使用utf-8编码的时候,由于utf能够表示这个汉字,所以没报错。

如果直接打印ss(us.encode(‘utf-8’)的返回值)会怎么样

>>> print ss


结果略有些奇怪,us.__str__(即直接打印us)的结果不一样,那么试试encoding = gbk呢?

>>> print us.encode('gbk')

U got it! 事实上也是如此,python会采用终端默认的编码(用locale.getdefaultlocale()查看,windows是为gbk)将unicode编码成str类型。

在Linux(终端编码为utf-8),结果如下:

>>> us= u'严'

>>> print us.encode('utf-8')

>>> print us.encode('gbk')

▒▒

>>> print us

>>>

注意上面的乱码!

unicode gbk之间的转换

在上上小节,介绍了unicode可以通过utf-8编码(encoding = utf-8),转换成utf-8表示的str,在上一节也可以看出unicode也可以通过gbk编码(encoding=gbk),转换成gbk表示的str。这里有点晕,留作第一个问题,后面解释

unicode与utf8之间的相互转换可以计算得知,但unicode与gbk之间的相互转换没有计算公式,就只能靠查表了,就是说有一张映射表,有某一个汉字对应的unicode表示与gbk表示的映射关系

>> us = u'严'

>>> us

u'\u4e25'

>>> us.encode('gbk')

'\xd1\xcf'

>>> us.encode('gb2312')

'\xd1\xcf'

>>> us.encode('gb18030')

'\xd1\xcf'

>>> s = '严'

>>> s

'\xd1\xcf'

>>>


从上不难看出,严的unicdoe编码是4e25,GBK编码是d1cf,因此us通过gbk编码就是d1cf。同样也能看到,GB18030,GBK,GB2312是兼容的

为什么print us.encode(‘utf-8’)打印出“涓”

ss = us.encode(‘utf-8’), ss是一个str类型,直接打印结果有点奇怪,一个“涓”字,那一个str类型的“涓”是哪些二进制组成的呢

>>> s = '涓'

>>> s

'\xe4\xb8'


可以看到,str类型的“涓”,其二进制是E4B8,跟’严’的utf8编码(E4B8A5)相差了一个A5,那么就是因为A5显示不出来,验证如下:

>>> print '--%s--' % ss

--?-


因此,只是碰巧显示了“涓”而已,事实上ss跟“”涓“”毫无关系

回答第一个问题:str类型到底是什么

在上上小节,提到了utf-8编码的str,与gbk编码的str,感觉有点绕。我们知道,一个汉字‘严’,可存储的编码格式可以是gbk(’xd1xcf’),也可以是utf-8(’xe4xb8xa5’),那么当我们在终端敲入这个汉字的时候,是哪一种格式呢?取决于终端默认编码。

windows上(默认终端编码为gbk):

>>> s = '严'

>>> s

'\xd1\xcf'


Linux上(默认终端编码为utf-8):


>>> a = '严'

>>> a

'\xe4\xb8\xa5'


同样一个汉字,同样都是Python中的str类型,在不同的编码格式下,其二进制是不一样的。因此,其长度也是不一样的,对于str类型,其长度是对应的字节长度。

也能看出gbk编码的字节长度一般小于utf-8,这也是gbk继续存在的一个原因。

这里,要强调一下,unicode的二进制形式是与终端的编码格式无关的!这个也不难理解。

unicode函数

str类型到unicode类型的转换,出了上面提到的str.decode,还有一个unicode函数。两个函数的签名为:

unicode(object[, encoding[, errors]])

Return the Unicode string version of object using one of the following modes:

 

str.decode([encoding[, errors]])

Decodes the string using the codec registered for encoding. encoding defaults to the default string encoding.


二者参数相同,事实上二者是等价的,encoding的默认值也是一样的,都是sys.getdefaultencoding()的结果。for example:


>>> s = '严'

>>> newuse = unicode(s)

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

UnicodeDecodeError: 'ascii' codec can't decode byte 0xd1 in position 0: ordinal not in range(128)

 

>>> newuse = unicode(s, 'utf-8')

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

UnicodeDecodeError: 'utf8' codec can't decode byte 0xd1 in position 0: invalid continuation byte

>>> newuse = unicode(s, 'gbk')

>>> newuse

u'\u4e25'


第一个UnicodeDecodeError,就是因为系统默认的编码是asill吗;第二个UnicodeDecodeError,是因为,s(str类型的实例)的编码取决于终端默认编码(即windows下的gbk),为了能打印出来,也就必须用gbk编码来表示这个str,因此只能查询gbk与unicode的映射表将s转换成unicode类型。

为啥调用sys.setdefaultencoding

在诸多Python代码中,都会看到这么一段:

import sys

reload(sys)

sys.setdefaultencoding('utf-8')


不难猜想,setdefaultencodinggetdefaultencoding是配对的,为啥要将系统的默认编码设置成utf-8,其实就是解决str到unicode的转换问题。

上一小节已经提到过,使用unicode函数将str类型转换成unicode类型时,要考虑两个因素:第一,str本身是什么编码的;第二,如果没有传入encoding参数,默认使用sys.getdefaultencoding。encoding参数必须与str本身的编码对应,否则就是UnicodeDecodeError。

写python代码的程序都知道,我们要在py文件第一行写上:

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

这句话的作用在于,告诉编辑器,该文件里面的所有str都采用utf-8编码,且存储文件的时候也是使用utf-8格式。

然后文件中就会使用下面的这种代码。

s='中文'

us=unicode(s)


使用unicode强制转换的时候,都不习惯带参数,为了保证encoding参数必须与str本身的编码一致,所以使用setdefaultencoding将系统默认编码设置为utf-8

乱码与UnicodeError

下面介绍几种常见的乱码与异常UnicodeError, 大多数乱码或者异常的原因在前面已经讲过了,同时,对于一些乱码,也试图给出可行的解决办法。

UnicodeError包括UnicodeDecodeError 与UnicodeEncodeError ,前者是decode也就是str转unicode的时候出了异常,后者则是encode也就是unicode转str的时候出了异常。

对于一个str,直接打印

例子就是上面反复提到的例子

>>> ss = us.encode('utf-8')

>>> print ss


如果一个str类型来自网络或者文件读取,最好先按照对端encode的方式先decode成unicode,然后再输出(输出的时候会自动转换成期望终端支持的编码格式的str)

编码范围无法包括的汉字

直接上例子

>>> newus = u'囍'

>>> newus

u'\u56cd'

>>> newus.encode('gbk')

'\x87\xd6'

>>> newus.encode('gb2312')

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

UnicodeEncodeError: 'gb2312' codec can't encode character u'\u56cd' in position 0: illegal multibyte sequence

>>>


可以看到,‘囍’字可以被gbk编码,但是不能被gb2312编码。

str转unicode的时候

在上面讲unicode函数的时候已经举过例子,会爆出UnicodeDecodeError 异常。

这个错误比较的原因,更多来自str到unicode的默认转换,比如一个str与一个unicode相加的时候:

>>> a = '严'

>>> b = u'严'

>>> c = a + b

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

UnicodeDecodeError: 'ascii' codec can't decode byte 0xd1 in position 0: ordinal not in range(128)

unicode 与 str相加,str会转换为unicode,使用默认的unicode(strobj, encoding = sys.getdefaultencoding())

看起来向unicode编码的字符串

某些情况下,我们打印出一个str类型,看到结果是’\u4e25’, 或者’u4e25’,对于这个字符串,是不是很眼熟,不错, ‘严‘的unicode编码就是u’u4e25’。仔细一看,只是在引号前面多了一个u(表示是一个unicode类型)。那么当我们看到一个’u4e25’的时候,怎么知道对应的汉字是什么?对于已知的这种格式的str,自然可以手动加一个u,然后在终端输出,但是如果是一个变量,需要自动转换成unicode呢,这个时候就可以使用python-specific-encodings中的unicode_escape

>>> s = '\u4e25'

>>> s

'\\u4e25'

>>> us = s.decode('unicode_escape')

>>> us

u'\u4e25'


十六进制格式的字符串

有时候,也会看到类似这样的str,’\xd1\xcf’, 看起来也很熟悉,跟汉字“严”的gbk编码’xd1xcf’很像,区别在于前者多了一个‘’, 这样就无法解释成一个十六进制了。解决办法是python-specific-encodings中的string_escape

>>> s='\\xd1\\xcf'

>>> s

'\\xd1\\xcf'

>>> print s

\xd1\xcf

>>> news = s.decode('string_escape')

>>> news

'\xd1\xcf'

>>> print news

给读者的一个问题

在这里留下一个问题:

u'严' == '严'

返回值是True 还是 False呢?当然这里故意省去了上下文环境,不过明确的说,在不同的编码环境下,答案是不一样的,原因都在上文中!

总结与建议

不管怎么样解释,python2.x中的字符编码还是一件让人头疼的事情,即使搞懂了,之后遇到了也可能忘记。对于这个问题,诸多建议如下:

第一:使用python3,就不用再纠结str于unicode了;但是这个很难开发者说了算;

第二:不要使用中文,注释什么的都用英文;理想很丰满,现实很难,只是导致大量的拼音;

第三:对于中文字符串,不要用str表示,而是用unicode表示;现实中也不好实施,大家都不愿意多写一个u

第四:只在传输,或者持久化的时候对unicode进行encode,相反的过程时decode

第五:对于网络接口,约定好编解码格式,强烈建议使用utf-8

第六:看到UnicodeXXXError不要慌,如果XXX是Encode,那么一定是unicode转str的时候出了问题;如果是Decode,一定是str转unicode的时候出了问题。

<think>在Windows环境下,Python 2.7Unicode编码问题通常表现为处理非ASCII字符时出现乱码或异常(如UnicodeEncodeError/UnicodeDecodeError)。以下是解决方案: ### 解决方案概览 1. **明确指定编码**:在文件读写、字符串处理时显式指定编码 2. **使用`unicode`字符串**:在代码中使用`u'...'`前缀创建Unicode字符串 3. **设置环境变量**:修改系统环境变量`PYTHONIOENCODING` 4. **使用`codecs`模块**:提供更强大的编码处理功能 5. **修改系统区域设置**(临时方案):调整Windows控制面板中的区域设置 ### 详细解决方案 #### 1. 文件读写时指定编码 ```python # 读取文件时指定编码 with open('file.txt', 'r') as f: content = f.read().decode('utf-8') # 将字节串解码为Unicode # 写入文件时指定编码 with open('file.txt', 'w') as f: f.write(u'你好世界'.encode('utf-8')) # 将Unicode编码为字节串 ``` #### 2. 使用`codecs`模块进行高级编码处理 ```python import codecs # 读取UTF-8文件 with codecs.open('file.txt', 'r', 'utf-8') as f: content = f.read() # 直接得到Unicode字符串 # 写入UTF-8文件 with codecs.open('file.txt', 'w', 'utf-8') as f: f.write(u'你好世界') # 直接写入Unicode字符串 ``` #### 3. 设置环境变量`PYTHONIOENCODING` 在运行Python脚本前设置环境变量: ```cmd set PYTHONIOENCODING=UTF-8 python your_script.py ``` 或在脚本开头添加: ```python import sys import os os.environ["PYTHONIOENCODING"] = "utf-8" sys.setdefaultencoding("utf-8") # 注意:该方法在Python启动后不可用,需在sitecustomize.py中设置 ``` #### 4. 强制重载默认编码(不推荐,临时方案) ```python import sys reload(sys) sys.setdefaultencoding('utf-8') # 可能掩盖潜在问题 ``` #### 5. 修改Windows系统区域设置(临时方案) 1. 进入控制面板 → 区域和语言 → 管理 2. 点击“更改系统区域设置” 3. 勾选“Beta版:使用Unicode UTF-8提供全球语言支持” 4. 重启系统 ### 最佳实践:完整的编码处理模板 ```python # -*- coding: utf-8 -*- import sys import codecs # 重定向标准输出到支持UTF-8编码的流 if sys.stdout.encoding != 'UTF-8': sys.stdout = codecs.getwriter('utf-8')(sys.stdout) # 重定向标准错误 if sys.stderr.encoding != 'UTF-8': sys.stderr = codecs.getwriter('utf-8')(sys.stderr) # 示例:安全打印Unicode字符串 def safe_print(s): if isinstance(s, unicode): s = s.encode(sys.stdout.encoding or 'utf-8', 'replace') print(s) # 测试 safe_print(u'中文测试') # 正常显示中文 ``` ### 常见错误及修复 #### 错误1:UnicodeEncodeError ```python # 错误示例 print(u'你好') # 报错:UnicodeEncodeError: 'ascii' codec can't encode characters... # 修复 print(u'你好'.encode('utf-8')) # 显式编码 ``` #### 错误2:文件路径编码问题 ```python # 错误示例 open(u'中文路径.txt') # 可能失败 # 修复:转换为短路径或使用Unicode API import win32api # 需安装pywin32 short_path = win32api.GetShortPathName(u'中文路径.txt') open(short_path) ``` #### 错误3:命令行参数编码 ```python # 获取命令行参数时解码 import sys # 在Windows下,命令行参数使用mbcs编码(即当前ANSI代码页) args = [arg.decode(sys.getfilesystemencoding()) for arg in sys.argv] ``` ### 永久解决方案(推荐) 创建`sitecustomize.py`文件并添加以下内容: ```python # 文件位置:<Python安装目录>\Lib\site-packages\sitecustomize.py import sys sys.setdefaultencoding('utf-8') # 设置默认编码 ``` 此文件会在Python启动时自动执行。 ### 注意事项 1. Python 3已默认支持Unicode,建议迁移到Python 3 2. 避免混合使用字节串(`str`)和Unicode字符串(`unicode`) 3. 在Windows控制台使用`chcp 65001`切换到UTF-8代码页 ### 相关问题扩展: §§相关问题§§: 1. Python 2Python 3在字符串处理上有哪些主要区别? 2. 如何在Python中正确处理文件路径(包括跨平台兼容)? 3. 什么是BOM(字节顺序标记)?如何处理带BOM的UTF-8文件? 4. 在Linux环境下,Python 2.7Unicode处理与Windows有何不同?</think>### 解决 Python 2.7 在 Windows 下的 Unicode 编码问题 在 Windows 环境下,Python 2.7Unicode 编码问题主要源于: 1. Windows 控制台默认使用 CP437/CP850(英文)或 GBK(中文)编码 2. Python 2.7 默认使用 ASCII 编码处理字符串 3. 文件操作和系统接口的编码不一致 以下是完整的解决方案: #### 解决方案 1:设置系统默认编码(推荐) ```python # -*- coding: utf-8 -*- import sys import os import codecs # 设置系统默认编码为 UTF-8 reload(sys) sys.setdefaultencoding('utf-8') # 修复 Windows 控制台输出 if sys.platform == 'win32': # 设置标准流的编码 sys.stdout = codecs.getwriter('utf-8')(sys.stdout, 'replace') sys.stderr = codecs.getwriter('utf-8')(sys.stderr, 'replace') # 设置控制台代码页为 UTF-8 os.system('chcp 65001 > nul') # 使用支持 Unicode 的字体 os.system('mode con: cols=120 lines=40') ``` #### 解决方案 2:文件操作最佳实践 ```python import codecs # 安全读取文件(自动处理 BOM) def safe_read_file(file_path): with open(file_path, 'rb') as f: content = f.read() # 移除可能的 UTF-8 BOM if content.startswith(codecs.BOM_UTF8): content = content[len(codecs.BOM_UTF8):] return content.decode('utf-8') # 安全写入文件 def safe_write_file(file_path, content): if isinstance(content, unicode): content = content.encode('utf-8') with open(file_path, 'wb') as f: f.write(content) # 示例使用 content = safe_read_file('input.txt') safe_write_file('output.txt', u'你好,世界!') ``` #### 解决方案 3:处理命令行参数和路径 ```python import sys def win32_unicode_argv(): """获取 Unicode 格式的命令行参数""" from ctypes import POINTER, byref, cdll, c_int, windll from ctypes.wintypes import LPCWSTR, LPWSTR GetCommandLineW = windll.kernel32.GetCommandLineW GetCommandLineW.argtypes = [] GetCommandLineW.restype = LPCWSTR CommandLineToArgvW = windll.shell32.CommandLineToArgvW CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)] CommandLineToArgvW.restype = POINTER(LPWSTR) cmd = GetCommandLineW() argc = c_int(0) argv = CommandLineToArgvW(cmd, byref(argc)) if argc.value > 0: return [argv[i] for i in range(argc.value)] return [] # 使用示例 if sys.platform == 'win32': sys.argv = win32_unicode_argv() ``` #### 解决方案 4:处理系统接口编码 ```python import os import sys import locale # 获取系统编码 def get_system_encoding(): try: encoding = locale.getpreferredencoding() or 'mbcs' # Windows 的特殊处理 if sys.platform == 'win32': if 'cp' in encoding.lower(): return encoding return 'mbcs' return encoding except: return 'utf-8' # 安全系统路径操作 def safe_system_path(path): if isinstance(path, unicode): return path.encode(get_system_encoding(), 'replace') return path # 示例:创建包含中文的目录 dir_name = u'中文目录' os.mkdir(safe_system_path(dir_name)) ``` #### 解决方案 5:使用兼容性包装器(推荐用于大型项目) ```python # unicode_safe.py import sys import os import codecs class UnicodeSafeEnvironment: def __enter__(self): self.original_stdout = sys.stdout self.original_stderr = sys.stderr self.original_argv = sys.argv # Windows 特殊处理 if sys.platform == 'win32': # 设置控制台代码页 os.system('chcp 65001 > nul') # 修复标准流 sys.stdout = codecs.getwriter('utf-8')(sys.stdout, 'replace') sys.stderr = codecs.getwriter('utf-8')(sys.stderr, 'replace') # 修复命令行参数 import ctypes GetCommandLineW = ctypes.windll.kernel32.GetCommandLineW GetCommandLineW.restype = ctypes.c_wchar_p CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW CommandLineToArgvW.restype = ctypes.POINTER(ctypes.c_wchar_p) cmd = GetCommandLineW() argc = ctypes.c_int() argv = CommandLineToArgvW(cmd, ctypes.byref(argc)) sys.argv = [argv[i] for i in range(argc.value)] return self def __exit__(self, exc_type, exc_value, traceback): sys.stdout = self.original_stdout sys.stderr = self.original_stderr sys.argv = self.original_argv # 使用示例 with UnicodeSafeEnvironment(): print(u"你好,世界!") # 正确处理Unicode # 其他需要Unicode安全的操作 ``` ### 关键问题解决策略 1. **控制台输出乱码**: - 使用 `chcp 65001` 设置控制台为 UTF-8 模式 - 重定向 `sys.stdout` 和 `sys.stderr` 使用 UTF-8 编码 2. **文件读写错误**: - 始终以二进制模式打开文件(`'rb'`/`'wb'`) - 显式进行 `decode('utf-8')` 和 `encode('utf-8')` 转换 - 处理 BOM(字节顺序标记)问题 3. **命令行参数问题**: - 使用 Windows API 直接获取 Unicode 格式的参数 - 避免直接使用 `sys.argv`(在 Windows 上它是 ANSI 编码) 4. **系统路径问题**: - 使用 `mbcs` 编码处理 Windows 系统路径 - 在需要时进行 Unicode 和字节串的显式转换 ### 永久性解决方案 对于持续开发项目,建议创建环境初始化脚本: ```python # sitecustomize.py (放在 Python 的 site-packages 目录) import sys import os def set_unicode_environment(): if sys.platform == 'win32' and sys.version_info[0] < 3: # 设置默认编码 reload(sys) sys.setdefaultencoding('utf-8') # 设置控制台编码 os.environ['PYTHONIOENCODING'] = 'utf-8' # 修复标准流 import codecs sys.stdout = codecs.getwriter('utf-8')(sys.stdout, 'replace') sys.stderr = codecs.getwriter('utf-8')(sys.stderr, 'replace') print(u"Unicode 环境已初始化!") set_unicode_environment() ``` ### 测试验证 ```python # 测试脚本 test_unicode.py # -*- coding: utf-8 -*- import os import sys def test_unicode(): # 测试控制台输出 print(u"中文测试 - Chinese Test") print(u"Русский тест - Russian Test") print(u"日本語テスト - Japanese Test") # 测试文件操作 with open('test_unicode.txt', 'w') as f: f.write(u"文件内容测试".encode('utf-8')) # 测试路径操作 dir_name = u"测试目录_テスト_тест" os.mkdir(dir_name) print(f"创建目录: {dir_name}") if __name__ == '__main__': test_unicode() ``` ### 相关问题扩展:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值