zoukankan      html  css  js  c++  java
  • UnicodeDecodeError: 'utf8' codec can't decode

    数据库存了些中文字符, 比如'处理脚本'这样的汉字, 结果导致一个python程序报错. 下面记录处理过程和相关结论.

    ===========================
    dal.py 程序片段,python 2.7,
    使用了pypyodbc模块
    ===========================
    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals

    def save(field_value):
        #logger.debug('%s'%type(field_value)) # trouble shooting  时加入的代码
        logger.debug('%s'%field_value)
        save_to_db(field_value)

       
    上面这个程序, 当field_value是汉字时, 报错:
    logger.debug报错, UnicodeDecodeError: 'utf8' codec can't decode byte 0xb4 in position 0: invalid start byte


    ===========================
    原因分析:
    ===========================
    我在logger.debug('%s'%field_value), 增加了如下代码, 发现field_value的类型是str, 非unicode.
    logger.debug('%s'%type(field_value))


    通过type(field_value)获知, field_value本身是str, 而不是unicode. 原因是field_value的值是在pypyodbc模块中初始化的, 并没有做自动做unicode转换.
    在我们的py程序中, 表达式'%s'%field_value将实例化一个字符串,因为加了import unicode_literals, 默认是字符串变量将是unicode.
    对于非unicode的field_value, python会自动按照utf-8做decode, 而实际上在DB中是按照GBK做的编码, 导致decode会报错.  
    那为什么python会自作主张按照utf-8做decode呢? 我原以为是"# -*- coding: utf-8 -*-"和py文件按照utf-8存储, 经试验, 推翻了我的猜测. 有知道的朋友, 请告知.



    ===========================
    测试了几个方法:
    ===========================
    方法1:
      将__future__ 的unicode_literals 拿掉后, logger中汉字显示是乱码, 但save()函数最终存到DB中的汉字, 却没有问题.
    方法2:
      将logger.debug('%s'%field_value)   换成 logger.debug(field_value), 不报错, 但logger中汉字显示是乱码
    方法3:
      从DB中取出汉字后, 马上对其按GBK 做decode, 将其转成unicode类型, 然后再调用save()方法, 问题彻底解决了.
      field_value=field_value_raw.decode('GBK')  # here field_value is unicode

     
     
    ===========================
    知识点:
    ===========================
    1. unicode是一个编码字符集, 即为每个字符设定了一个对应的编码表, 至于如何存储字符的编码, 并没有做规定.  utf-8/utf-16等编码即是unicode的具体存储实现方式, 其中utf-8也是最常用的方式.
    2. GB2312、GBK、 GB18030既是编码字符集, 也是存储方式.
    3. 如果py文件加了# -*- coding: utf-8 -*-, 字符串默认将以utf-8编码存储, 而且Eclipse也很智能将文件也按照UTF-8存放.
    4. 如果py文件加了from __future__ import unicode_literals, 本py文件中声明的字符串将按照unicode 类型.  
    5. 对于py程序从DB取出汉字的过程, 涉及到两个不同世界的存储方式, 一般都需要先做decode, 将其转为unicode.
    6. 如果使用的是Oracle数据库, 汉字若存在varchar字段, 要看NLS_CHARACTERSET设定值, 比如ZHS16GBK, 需要做decode('GBK');  如果汉字存在NVARCHAR中, 要看 NLS_NCHAR_CHARACTERSET 的设定, 因为NLS_NCHAR_CHARACTERSET多设定为UTF8, python程序就不需要做转换了.



    ===========================
    延伸阅读:
    ===========================
    如joel所讲, 每个开发人员都应该清楚unicode和char set知识, 推荐阮一峰的文章:
    http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
    http://www.pythonclub.org/python-basic/encode-detail

    字体编辑用中日韩汉字Unicode编码表
    http://www.chi2ko.com/tool/CJK.htm

    SO上unicode_literals的问答
    http://stackoverflow.com/questions/809796/any-gotchas-using-unicode-literals-in-python-2-6
  • 相关阅读:
    C++ template —— 模板中的名称(三)
    关于烂代码的那些事(下)
    关于烂代码的那些事(中)
    关于烂代码的那些事(上)
    比尔的村庄:创业是选择做赚钱的事,还是值钱的事?
    C++ template —— 深入模板基础(二)
    依赖倒置,控制反转,依赖注入
    JAVA中限制接口流量、并发的方法
    SVN同步时忽略特定文件或文件夹
    MySQL中查询表及索引大小的方法
  • 原文地址:https://www.cnblogs.com/harrychinese/p/UnicodeDecodeError_utf8_codec_decode_issue.html
Copyright © 2011-2022 走看看