zoukankan      html  css  js  c++  java
  • python2.7 关于打印中文的各种方法

    目录

    正文

      在开发过程中,我们经常需要打印一些变量的值,便于调试。这个时候就会发现如果在dict list这些容器中,如果包含中文字符,不管是str类型,还是unicode类型,都打印不出来。如下:

    >>> print {'name': '张三'}
    {'name': 'xd5xc5xc8xfd'}
    >>> print {'name': u'张三'}
    {'name': u'u5f20u4e09'}

      当然,作为凡人的我是在无法脑补这些十六进制的意思,每次转移一下也很麻烦,有没有办法一劳永逸呢。google了一把,发现还是有很多姿势的。

      注意:本文实验主要基于win7,Python2.7,运行环境如下

    >>> import sys,locale
    >>> sys.getdefaultencoding()
    'ascii'
    >>> locale.getdefaultlocale()
    ('zh_CN', 'cp936')
    >>> sys.stdin.encoding
    'cp936'
    >>> sys.stdout.encoding
    'cp936'

       本文地址:http://www.cnblogs.com/xybaby/p/7854126.html

    str类型的中文

      首先让我们分析一下为什么无法包含中文的container(dict list tuple)

     >>> data = {'严': 1, 2: ['如'], 3:'玉'}

    >>> data
    {2: ['xc8xe7'], 3: 'xd3xf1', 'xd1xcf': 1}
    >>> print data
    {2: ['xc8xe7'], 3: 'xd3xf1', 'xd1xcf': 1}
    >>> print data[3]

       上面data在key value中包含中文,而且也有嵌套的list,后文都使用这个data

      可以看到不管是直接输出data(调用dict.__repr__),还是print data(调用dict.__str__),都无法输出中文,而是像str.__repr__的结果。即调用容器的__str__时,事实上调用的是容器元素的__repr__方法,这个很好验证:

    >>> class OBJ(object):
    ... def __str__(self):
    ...    return 'OBJ str'
    ... def __repr__(self):
    ...    return 'OBJ repr'
    ...
    >>> lst = [OBJ()]
    >>> print lst
    [OBJ repr]
    >>>

      OBJ这个自定义的类,__str__ __repr__的方法实现不一样,当作为container(list)的元素时,明显调用的是OBJ.__repr__

      在stackoverflow上的一个问题print-a-list-that-contains-chinese-characters-in-python给出了答案

    When you print foo, what gets printed out is str(foo).
    However, if foo is a list, str(foo) uses repr(bar) for each element bar, not str(bar).

      当然,这个问题早就被人发现了,在PEP3140 str(container) should call str(item), not repr(item) ,在这个提议中,就建议在打印容器的时候,使用__str__而不是__repr__。但是被Guido(Python之父)无情的拒绝了,原因是:

    Guido said this would cause too much disturbance too close to beta

      虽然提议被reject了,但是需求还是照样存在的啊,于是有了各种解决办法

    第一种姿势:逐个打印

      直接print容器中的元素

    >>> lst = ['张三', '李四']
    >>> print '[' + ', '.join(["asdf", "中文"]) + ']'
    [asdf, 中文]
    >>> for k, v in {'name': '张三'}.items():
    ... print k, v
    ...
    name 张三

      对于简单的容器对象,还是很方便的,但是对于嵌套的容器对象,比如上面data的例子,就很麻烦了

    第二种姿势: json dumps

      这个方法在网上推荐得较多

    >>> import json
    >>> dumped_data = json.dumps(data, encoding = 'gbk', ensure_ascii=False)
    >>> print dumped_data
    {"2": ["如"], "3": "玉", "严": 1}

      可以看到,虽然打印出了中文,但是2 3都被加上了引号,感觉怪怪的

      需要注意的是上面的两个参数(encoing ensure_ascii), 这两个参数都有默认参数(encoding = 'utf-8', ensure_ascii=True),跟我们这里使用的都不一样。

    >>> dumped_data = json.dumps(data)
      Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "D:Python27.9libjson\__init__.py", line 243, in dumps
        return _default_encoder.encode(obj)
      File "D:Python27.9libjsonencoder.py", line 207, in encode
        chunks = self.iterencode(o, _one_shot=True)
      File "D:Python27.9libjsonencoder.py", line 270, in iterencode
        return _iterencode(o, 0)
    UnicodeDecodeError: 'utf8' codec can't decode byte 0xc8 in position 0: invalid continuation byte

      当然,为什么这里爆出了UnicodeDecodeError,可以参考这篇文章《不想再被鄙视?那就看进来! 一文搞懂Python2字符编码

      ensure_ascii参数也很关键

    >>> dumped_data = json.dumps(data, encoding = 'gbk')
    >>> print dumped_data
    {"2": ["u5982"], "3": "u7389", "u4e25": 1}

      python document是有描述的;

    If ensure_ascii is True (the default), all non-ASCII characters in the output are escaped with uXXXX sequences, and the result is a str instance consisting of ASCII characters only.

    第三种姿势: repr string_escape

    >>> decoded_data = repr(data).decode('string_escape')
    >>> print decoded_data
    {2: ['如'], 3: '玉', '严': 1}

      既然repr的输出是十六进制的str,那么就可以使用string_escape进行转换,具体也可以参见上文

    第四种姿势:PEP3140

      虽然PEP3140被reject了,但我们还是可以利用其思想吧,那就是强制调用str.__str__而不是str.__repr__

    复制代码
     1 class ForceStr(str):
     2     def __repr__(self):
     3         return super(ForceStr, self).__str__()
     4 
     5 def switch_container( data ):
     6     ret = None
     7     if isinstance(data, str):
     8         ret = ForceStr(data)
     9     elif isinstance(data, list) or isinstance(data, tuple):
    10         ret = [switch_container(var) for var in data]
    11     elif isinstance(data, dict):
    12         ret = dict((switch_container(k), switch_container(v)) for k, v in data.iteritems())
    13     else:
    14         ret = data
    15     return ret
    复制代码


    >>> switched_data = switch_container(data)
    >>> print switched_data
    {2: [如], 3: 玉, 严: 1}
    >>> switched_data
    {2: [如], 3: 玉, 严: 1}

      ForceStr继承自str,然后ForceStr.__repr__调用str.__str__。然后递归将容器里面的str类型的元素替换成ForceStr。可以看到,能够顺序打印出中文,格式也没有问题

    unicode类型的中文

      基本姿势于上一章节是一样的,下面直接给出答案

      同上第二种姿势

    >>> udata = {u'严': 1, 2: [u'如'], 3:u'玉'}
    >>> print json.dumps(udata, encoding = 'gbk', ensure_ascii=False)
    {"2": ["如"], "3": "玉", "严": 1}

      同上第三种姿势

    >>> print repr(udata).decode('unicode_escape')
    {2: [u'如'], 3: u'玉', u'严': 1}
    >>>

      同上第四种姿势

    复制代码
     1 def switch_container( data ):
     2     ret = None
     3     if isinstance(data, unicode):
     4         ret = ForceStr(data.encode(sys.stdout.encoding))
     5     elif isinstance(data, list) or isinstance(data, tuple):
     6         ret = [switch_container(var) for var in data]
     7     elif isinstance(data, dict):
     8         ret = dict((switch_container(k), switch_container(v)) for k, v in data.iteritems())
     9     else:
    10         ret = data
    11     return ret
    复制代码

    >>>
    >>> print switch_container(udata)
    {2: [如], 3: 玉, 严: 1}

    当str与unicode中文并存时

    同上第二种姿势

    >>> data[4] = u'啊'
    >>> print json.dumps(data, encoding = 'gbk', ensure_ascii=False)
    {"2": ["如"], "3": "玉", "4": "啊", "严": 1}

    同上第三种姿势

    >>> print repr(data).decode('string_escape')
    {2: ['如'], 3: '玉', 4: u'u554a', '严': 1}

      呃,unicode中文打印不出来

    >>> print repr(data).decode('unicode_escape')
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    UnicodeEncodeError: 'gbk' codec can't encode character u'xc8' in position 6: illegal multibyte sequence
    >>>

      擦,也许有正确的姿势,不过我没有试出来

      同上第四种姿势

    复制代码
     1 def switch_container( data ):
     2     ret = None
     3     if isinstance(data, str):
     4         ret = ForceStr(data)
     5     elif isinstance(data, unicode):
     6         ret = ForceStr(data.encode(sys.stdout.encoding))
     7     elif isinstance(data, list) or isinstance(data, tuple):
     8         ret = [switch_container(var) for var in data]
     9     elif isinstance(data, dict):
    10         ret = dict((switch_container(k), switch_container(v)) for k, v in data.iteritems())
    11     else:
    12         ret = data
    13     return ret
    复制代码

    >>> print switch_container(data)
    {2: [如], 3: 玉, 4: 啊, 严: 1}

    总结

      json.dumps版本还算可以,能够处理str中文,unicode中文, str与unicode中文并存三种情况,不过显示结果与真实有点差异

      string_escape(unicode_escape)只使用只有str(unicode)中文的情况,使用较为受限

      自己实现的switch_container版本,能够友好支持str中文,unicode中文,str与unicode中文并存三种情况

      str与unicode并存真是一件蛋疼的事情!

  • 相关阅读:
    Windows Server 2012配置开机启动项
    Windows Server 2019 SSH Server
    NOIP2017 senior A 模拟赛 7.7 T1 棋盘
    Noip 2015 senior 复赛 Day2 子串
    Noip 2015 senior复赛 题解
    Noip 2014 senior Day2 解方程(equation)
    Noip 2014 senior Day2 寻找道路(road)
    Noip 2014 senior Day2 无线网络发射器选址(wireless)
    Noip2014senior复赛 飞扬的小鸟
    Noip 2014 senior 复赛 联合权值(link)
  • 原文地址:https://www.cnblogs.com/pyxiaomangshe/p/7864903.html
Copyright © 2011-2022 走看看