zoukankan      html  css  js  c++  java
  • python2.7 内置ConfigParser支持Unicode读写

    1 python编码基础

    对应 C/C++ 的 char 和 wchar_t, Python 也有两种字符串类型,str 与 unicode:

    str与unicode
     1 # -*- coding: utf-8 -*-
     2 # file: example1.py
     3 import string
     4 
     5 # 这个是 str 的字符串
     6 s = '关关雎鸠'
     7 
     8 # 这个是 unicode 的字符串
     9 u = u'关关雎鸠'
    10 
    11 print isinstance(s, str)      # True
    12 print isinstance(u, unicode)  # True
    13 
    14 print s.__class__   # <type 'str'>
    15 print u.__class__   # <type 'unicode'>stryu

    前面的申明:# -*- coding: utf-8 -*- 表明,上面的 Python 代码由 utf-8 编码。
    两个 Python 字符串类型间可以用 encode / decode 方法转换:

    1 # 从 str 转换成 unicode
    2 print s.decode('utf-8')   # 关关雎鸠
    3 
    4 # 从 unicode 转换成 str
    5 print u.encode('utf-8')   # 关关雎鸠
    encode/decode转换

    为什么从 unicode 转 str 是 encode,而反过来叫 decode? 

    因为 Python 认为 16 位的 unicode 才是字符的唯一内码,而大家常用的字符集如 gb2312,gb18030/gbk,utf-8,以及 ascii 都是字符的二进制(字节)编码形式。把字符从 unicode 转换成二进制编码,当然是要 encode。

    反过来,在 Python 中出现的 str 都是用字符集编码的 ansi 字符串。Python 本身并不知道 str 的编码,需要由开发者指定正确的字符集 decode。

    (补充一句,其实 Python 是可以知道 str 编码的。因为我们在代码前面申明了 # -*- coding: utf-8 -*-,这表明代码中的 str 都是用 utf-8 编码的,我不知道 Python 为什么不这样做。)
    如果用错误的字符集来 encode/decode 会怎样?

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

     为什么 Python 这么容易出现字符串编/解码异常? 

    这要提到处理 Python 编码时容易遇到的两个陷阱。第一个是有关字符串连接的: 

     1 # -*- coding: utf-8 -*-
     2 # file: example2.py
     3 
     4 # 这个是 str 的字符串
     5 s = '关关雎鸠'
     6 
     7 # 这个是 unicode 的字符串
     8 u = u'关关雎鸠'
     9 
    10 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 有中文,一定会出现错误。

    除了字符串连接,% 运算的结果也是一样的:

     1 # 正确,所有的字符串都是 str, 不需要 decode
     2 "中文:%s" % s   # 中文:关关雎鸠
     3 
     4 # 失败,相当于运行:"中文:%s".decode('ascii') % u
     5 "中文:%s" % u  # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
     6 
     7 # 正确,所有字符串都是 unicode, 不需要 decode
     8 u"中文:%s" % u   # 中文:关关雎鸠
     9 
    10 # 失败,相当于运行:u"中文:%s" % s.decode('ascii')
    11 u"中文:%s" % s  # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
    %运算

    其实,sys.getdefaultencoding() 的值是可以用“后门”方式修改的,我不是特别推荐这个解决方案,但是还是贴一下,因为后面有用:

     1 # -*- coding: utf-8 -*-
     2 # file: example3.py
     3 import sys
     4 
     5 # 这个是 str 的字符串
     6 s = '关关雎鸠'
     7 
     8 # 这个是 unicode 的字符串
     9 u = u'关关雎鸠'
    10 
    11 # 使得 sys.getdefaultencoding() 的值为 'utf-8'
    12 reload(sys)                      # reload 才能调用 setdefaultencoding 方法
    13 sys.setdefaultencoding('utf-8')  # 设置 'utf-8'
    14 
    15 # 没问题
    16 s + u  # u'u5173u5173u96ceu9e20u5173u5173u96ceu9e20'
    17 
    18 # 同样没问题
    19 "中文:%s" % u   # u'u4e2du6587uff1au5173u5173u96ceu9e20'
    20 
    21 # 还是没问题
    22 u"中文:%s" % s  # u'u4e2du6587uff1au5173u5173u96ceu9e20'
    setdefaultencoding

    可以看到,问题魔术般的解决了。但是注意! sys.setdefaultencoding() 的效果是全局的,如果你的代码由几个不同编码的 Python 文件组成,用这种方法只是按下了葫芦浮起了瓢,让问题变得复杂。
    另一个陷阱是有关标准输出的。(另一个陷阱跟本文章关系不大,请参考这一节的原文:http://in355hz.iteye.com/blog/1860787

    2 python读写ini配置文件

    以上介绍了基础python编解码知识,下面具体说明ConfigParser如果管理ini配置文件
    配置文件编码为UTF-8,内容如下:

    1 [section]
    2 option=中文字符串
    cfg.ini

    可以通过Notepad++来查看cfg.ini文件的编码方式

    ConfigParser可以方便的读取ini配置文件,但是当重新写入时会遇到问题

     1 import codecs
     2 import ConfigParser
     3 
     4 cfgfile="cfg.ini"
     5 
     6 config = ConfigParser.ConfigParser()
     7 config.readfp(codecs.open(cfgfile, "r", "utf-8"))
     8 value = config.get("section","option")
     9 #config.write(open("cfg2.ini", "w+"))
    10 config.write(codecs.open("cfg2.ini", "w+", "utf-8"))
    write error

    跟踪了一下,在ConfigParser模块的如下位置出现问题:

    具体来说是str(value)出错了,因为在例子中的value值为“中文字符串”这已经超出ascii编码的处理范围,我的解决方法是重新实现写入操作,请参考代码:

     1 #!/usr/bin/python
     2 # -*- coding: utf-8 -*-
     3 #-------------------------------------------------------------------------------
     4 # Name:
     5 # Purpose:
     6 #
     7 # Author:      ZWW
     8 #
     9 # Created:     19/01/2014
    10 # Copyright:   (c) ZWW 2014
    11 # Licence:     <your licence>
    12 #-------------------------------------------------------------------------------
    13 import codecs
    14 import ConfigParser
    15 import types
    16 import sys
    17 
    18 cfgfile="cfg.ini"
    19 
    20 def ini_set(sec,key,value):
    21     try:
    22         config.readfp(codecs.open(cfgfile, "r", "utf-8"))
    23         if not config.has_section(sec):
    24             temp = config.add_section(sec)
    25         config.set(sec, key, value)
    26     except Exception as e:
    27         print("error",str(e))
    28     file = codecs.open(cfgfile, "w", "utf-8")
    29     sections=config.sections()
    30     for section in sections:
    31         #print section
    32         file.write("[%s]
    " % section)
    33         for (key, value) in config.items(section):
    34             if key == "__name__":
    35                 continue
    36             if type(value) in (type(u'') , type('')):
    37                 file.write(key+"="+value)
    38             elif type(value) == type(1):
    39                 optStr="%s=%d"%(key,value)
    40                 file.write(optStr)
    41             elif type(value) == type(1.5):
    42                 optStr="%s=%f"%(key,value)
    43                 file.write(optStr)
    44             else:
    45                 print "do not support this type"
    46                 print value
    47             file.write("
    ")
    48     file.close()
    49 
    50 if __name__=="__main__":
    51     config = ConfigParser.ConfigParser()
    52     config.readfp(codecs.open(cfgfile, "r", "utf-8"))
    53     value = config.get("section","option")
    54     #config.write(open("cfg2.ini", "w+"))
    55     #config.write(codecs.open("cfg2.ini", "w+", "utf-8"))
    56     print value
    57 
    58     str='中文'
    59     print str
    60     print type(str)
    61     print repr(str)
    62 
    63     utf8str = str.decode('utf-8')
    64     print utf8str
    65     print type(utf8str)
    66     print repr(utf8str)
    67 
    68     ini_set("section","option",utf8str)
    test.py

    执行完程序后输出如下:

     1 1 >>> 
     2 2 中文字符串
     3 3 涓�枃
     4 4 <type 'str'>
     5 5 'xe4xb8xadxe6x96x87'
     6 6 中文
     7 7 <type 'unicode'>
     8 8 u'u4e2du6587'
     9 9 >>> 
    10 output of test.py
    Output of test.py

    新的cfg.ini内容如下:

  • 相关阅读:
    移动端工作心得
    小程序初探
    你可能会用到的"奇技赢巧"
    一个知乎日报pwa
    Progressive Web Applications
    Java中的基本数据类型以及装箱、拆箱
    用sql获得指定记录的空段数目和字段名称--实在想不通,这种场景应用在哪
    putIfAbsent,一个字段为空的话,将该字段设置为指定值
    PO、VO、BO、POJO、DAO、DTO都是什么对象
    Java中字符串连接符(+)和append的区别
  • 原文地址:https://www.cnblogs.com/zhaoweiwei/p/ConfigParser.html
Copyright © 2011-2022 走看看