python中怎么判断编码的兼容性?

在python中,strjoin=str1+str2,如果str1(ascii编码)和str2(utf-8编码)的编码方式不一致,比如,那么strjoin将是个'奇怪的字符串',自身就有两种不同的编码.
这种情况往往导致strjoin显示为乱码.
在ruby中,当执行strjoin=str1+str2时,ruby会检验str1编码和str2的编码,以及这两个编码是否兼容(例如,utf-8编码就可以兼容ascii编码),如果兼容,就统一使用兼容性的编码作为strjoin的编码.
检查编码兼容性的伪代码如下:

Encoding.compatible?(coding1, coding2)

如果coding1兼容coding2编码,则返回coding1;如果coding2兼容coding1,则返回coding2;如果coding1和coding2没有兼容性,则返回false.

我想问的是,python中是否有类似的方法或者模块,来检查编码的兼容性?
多谢!

阅读 3.5k
1 个回答

以Python2.7为例说明一下Python在字符串拼接时的编码机制。

Python2.7字符串有两种类型unicode和str,unicode和unicode字符串,str和str字符串
拼接时最终结果还是原来的类型;当unicode和str进行拼接时,会将str类型的字符串按
default encoding解码为unicode后再拼接。
str类型的字符串实际上可以是任意编码的字节串,解码和拼接过程不会做实际的编码检
查,也没有信息去检查(有编码检测的模块如chardet,但也只会给一个猜测可信度)。

下面是详细描述,稍微有点冗长:

1 Python2.7字符串类型

Python2.7字符串有两种类型unicode和str(可以认为是字节串)

>>> a = 'abc'
>>> type(a)
<type 'str'>
>>> a = u'abc'
<type 'unicode'>

>>> b = '中国' # 我的终端是utf-8编码,所以b是utf-8编码的字节串
<type 'str'>
>>> b = u'中国'
<type 'unicode'>

2 Python2.7字符串编解码

unicode编码的字符串可以通过encode函数编码成各种其他编码的字符串(类型为str的字节串)


>>> a = u'中国'
>>> a_utf8 = a.encode('utf-8')
>>> a_utf8, type(a_utf8)
('\xe4\xb8\xad\xe5\x9b\xbd', <type 'str'>)
>>> a_gbk = a.encode('gbk')
>>> a_gbk
('\xd6\xd0\xb9\xfa', <type 'str'>)

其他编码的字节串可以通过decode函数解码成unicode字符串:


>>> a_unicode = a_utf8.decode('utf-8')
>>> a_unicode, type(a_unicode)
(u'\u4e2d\u56fd', <type 'unicode'>)
>>> a_unicode = a_gbk.decode('gbk')
>>> a_unicode, type(a_unicode)
(u'\u4e2d\u56fd', <type 'unicode'>)

3 Python2.7字符串拼接

字符串拼接用到Python的default encoding。

可以通过下面代码获取default encoding:

>>> import sys
>>> sys.getdefaultencoding()    # 默认缺省编码是ascii
'ascii'

可以通过下面代码设置default encoding:

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

字符串有两种类型unicode和str,所以拼接一共有4中场景:

3.1 unicode + unicode

    这个比较简单,直接拼接,结果仍然是unicode
    >>> str1 = u'a'
    >>> str2 = u'中国'
    >>> strjoin = str1 + str2
    >>> strjoin
    u'a\u4e2d\u56fd'
    >>> print strjoin
    a中国

3.2 unicode + str和str + unicode (行为一致)

首先将类型为str的字符串使用default encoding解码为unicode,然后拼接。
解码过程不会检查str字节串实际是什么编码,也没有信息可以检查。
假设str1为unicode,str2为str,则
str1 + str2 会转变为 str1 + str2.decode(sys.getdefaultencoding())


    # 默认default encoding为ascii
    >>> str1 = u'a'
    >>> str2 = 'b'
    >>> strjoin = str1 + str2
    >>> strjoin
    u'ab'

    >>> str1 = u'a'
    # str2为utf-8编码的字节串,类型为str;
    # 为了明显起见也可以改为 str2 = u'中国'.encode('utf-8')
    >>> str2 = '中国'
    >>> strjoin = str1 + str2
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
    
    # 这里的错误发生在对str2.decode('ascii'),
    # 如果将default encoding改为utf-8就不会有问题了
    
    >>> import sys
    >>> reload(sys)
    >>> sys.setdefaultencoding('utf-8')
    >>> strjoin = str1 + str2
    >>> strjoin
    u'a\u4e2d\u56fd' 
    >>> print strjoin
    a中国
    # 实际在多个字符串拼接时,只要其中有unicode字符串,其他str字符串都会先解码为unicode后再进行拼接。
    

3.3 str + str

这个也简单,直接拼接,结果仍然是str。
不会去检查str字节串实际是什么编码。

    >>> str1 = 'a'
    >>> str2 = '中国'
    >>> strjoin = str1 + str2
    >>> strjoin, type(strjoin)
    ('a\xe4\xb8\xad\xe5\x9b\xbd', <type 'str'>)

    >>> str1 = u'中国'.encode('utf-8')
    >>> str2 = u'中国'.encode('gbk')
    >>> str1, str2
    ('\xe4\xb8\xad\xe5\x9b\xbd', '\xd6\xd0\xb9\xfa')
    >>> strjoin = str1 + str2
    '\xe4\xb8\xad\xe5\x9b\xbd\xd6\xd0\xb9\xfa'
    >>> print strjoin   
    中国?й?
    # 由于系统标准输出编码为UTF-8,所以gbk编码的字节串显示为乱码
    >>> sys.stdout.encoding
    'UTF-8'
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏