• python编码问题(2)


    1 编码、解码

      如同密码领域一样,从明文到密码是加密,从密码到明文是解密。在python中,(编码:unicode-->str;) 解码(str-->unicode).

          既然是编码,那么就和密码领域一样,编码和解码自然涉及到编码/解码方案(对应加密或者解密算法),unicode相当于明文。在python中,编码函数是encode(),解码函数是decode()。需要注意的一点是,如果我们调用str.encode(),这里涉及到一个隐士的类型转化,会现将str转化成unicode,才能进行编码,这也是不太容易理解的地方。所以,str.encode()实际上就等价于str.decode(sys.defaultencoding).encode().而sys.defaultencoding一般是ascii,它是不能用来编码中文字符的。  字符串在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成其他编码。代码中字符串的默认编码与代码文件本身的编码一致。

    2 字符串

    在Python中有两种默认的字符串:str和Unicode:

    • str字符串本质上是一个字节流,是原字符经过编码之后的一个个字节,但它并不存储具体的编码方式。
    • Unicode字符串本质上是一个Unicode对象,它内部使用了特定的编码,但是对程序员透明。事实上Python中并没有Unicode字符串,但为了理解方便,以下所说的Unicode字符串均指的是Unicode对象。

     

    # -*- coding: utf-8 -*-
    # file: example1.py
    import string
    
    # 这个是 str 的字符串
    s = '关关雎鸠'
    
    # 这个是 unicode 的字符串
    u = u'关关雎鸠'
    
    print isinstance(s, str)      # True
    print isinstance(u, unicode)  # True
    
    print s.__class__   # <type 'str'>
    print u.__class__   # <type 'unicode'>

    如1中所说,两个 Python 字符串类型间可以用 encode / decode 方法转换

    # 从 str 转换成 unicode
    print s.decode('utf-8')   # 关关雎鸠
    
    # 从 unicode 转换成 str
    print u.encode('utf-8')   # 关关雎鸠

    如果用错误的字符集来 encode/decode 会怎样?

    # 用 ascii 编码含中文的 unicode 字符串
    u.encode('ascii')  # 错误,因为中文无法用 ascii 字符集编码
                       # UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
    
    # 用 gbk 编码含中文的 unicode 字符串
    u.encode('gbk')  # 正确,因为 '关关雎鸠' 可以用中文 gbk 字符集表示
                     # 'xb9xd8xb9xd8xf6xc2xf0xaf'
                     # 直接 print 上面的 str 会显示乱码,修改环境变量为 zh_CN.GBK 可以看到结果是对的
    
    # 用 ascii 解码 utf-8 字符串
    s.decode('ascii')  # 错误,中文 utf-8 字符无法用 ascii 解码
                       # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
    
    # 用 gbk 解码 utf-8 字符串
    s.decode('gbk')  # 不出错,但是用 gbk 解码 utf-8 字符流的结果,显然只是乱码
                     # u'u934fu51b2u53e7u95c6u5ea8u7b2d'

    几个小问题

    (1) 字符串拼接

    # -*- coding: utf-8 -*-
    # file: example2.py
    
    # 这个是 str 的字符串
    s = '关关雎鸠'
    
    # 这个是 unicode 的字符串
    u = u'关关雎鸠'
    
    s + u  # 失败,UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)

    在进行同时包含 str 与 unicode 的运算时,Python 一律都把 str 转换成 unicode 再运算,当然,运算结果也都是 unicode。

    由于 Python 事先并不知道 str 的编码,它只能使用 sys.getdefaultencoding() 编码去 decode。在我的印象里,sys.getdefaultencoding() 的值总是 'ascii' ——显然,如果需要转换的 str 有中文,一定会出现错误。

    (2)字符串长度

    str类型的字符串长度为其编码后的长度,并不是字符串本身长度。Unicode字符串的类型为"unicode",使用len得到的长度为2,表示了这个字符串的真正长度。

    >>> s = '你好'
    >>> type(s)
    <type 'str'>
    >>> s
    'xc4xe3xbaxc3'
    >>> print s
    你好
    >>> len(s)
    4
    >>> u = u'你好'
    >>> type(u)
    <type 'unicode'>
    >>> u
    u'u4f60u597d'
    >>> print u
    你好
    >>> len(u)
    2

    为了减少不必要的麻烦,在实际的编程工作中,我们可以按如下方法进行:

    1. 最先解码。首先要将输入的字符字节流解码为Unicode对象,方便后续如计数、切片等操作的进行。
    2. 最后编码。最后将处理后的结果输出到其他文件或者数据库的时候才将Unicode对象进行编码,以满足特定的存储显示需求。
    3. 默认使用utf-8编码。使用UTF-8编码可以处理任何Unicode字符,具有更好地通用性,尽量用来替代默认的ASCII或者GBK编码,但是要注意Windows中默认编码为GBK或者其他类似GBK的编码的情况

    3 文件编码

      文件编码指的是具体的代码文本在保存时使用的编码方法。如:s='中文',如果是在utf8的文件中,该字符串就是utf8编码,如果是在gb2312的文件中,则其编码为gb2312。

      如果声明的编码(指-*- coding: UTF-8 -*-这样的申明)和文件实际的编码不一致的话,很可能会出现乱码甚至出错等问题。原因是对于类似于u’你好’这样声明的字符串,在源代码文件里经过了该文件指定编码方法的编码被保存成字节流,程序运行时读取到这个字节流,就会使用编码声明中的编码去解码得到原始字符串,然后对这个原始字符串再用Unicode的内部编码方案进行存储。而对于’你好’这样不带u的字符串,则表示字节流,此时会直接用文件编码后的字节数组进行表示,不需要使用编码声明中的编码方案进行解码。例如,建立如下内容的脚本文件然后保存成ANSI编码:

    # -*- coding: UTF-8 -*-
    
    s = '你好'
    
    print repr(s), s
    
    u = u'你好'
    
    print repr(u), u

    运行后得到错误

    SyntaxError: (unicode error) ‘utf8′ codec can’t decode byte 0xc4 in position 0: invalid continuation byte

    但把脚本改成:

    # -*- coding: UTF-8 -*-
    
    s = '你好'
    
    print repr(s), s

    则可以得到以下结果:

    'xc4xe3xbaxc3' 你好 #原文件使用的是ANSI编码,所以得到GBK编码,GBK编码可以正常显示

     参考资料

    http://in355hz.iteye.com/blog/1860787

    http://5319188.blog.51cto.com/5309188/1112505

    http://noalgo.info/578.html

  • 相关阅读:
    优雅高效的MyBatis-Plus工具快速入门使用
    mybatis中#{}和${}的区别
    异常处理com.sun.image.codec.jpeg.JPEGImageEncoder
    图片压缩之-JPEGCodec失效替换方案
    Bugly实现app全量更新
    MyBatis下MySqL用户口令不能为空
    java.lang.OutOfMemoryError: PermGen space及其解决方法
    Hibernate or 的用法
    如何理解<base href="<%=basePath%>"
    小程序 wx.request ajax示例
  • 原文地址:https://www.cnblogs.com/yxzfscg/p/4861108.html
Copyright © 2020-2023  润新知