最后更新于 .

最近在使用django的过程中,发现之前对中文编码的理解并不怎么正确,在此记录一下。

1.在所有需要显式使用中文的地方加上#-*- coding: UTF-8 -*-标识,(包括注释中的中文和代码中字符串的中文)
2.django在db中存储的数据是经过encode的,但是通过模型取出的数据,是经过decode的。
3.python中在进行字符串连接的时候,必须保证所有的子字符串编码或者未编码一致

OK,先从最简单的赋值开始。如下代码:

#!/usr/bin/python
#-*- coding: UTF-8 -*-
import logging
logging.basicConfig(level=logging.DEBUG,
        format='%(asctime)s %(levelname)s %(message)s',
        filename='log.txt',
        filemode='a+')
x = '我爱你'
logging.info(x)
print x

输出是:

我爱你

log.txt中的结果是:

2010-06-25 17:50:51,019 INFO 我爱你

x是未经过decode的,所以在print的时候,系统会自动根据编码格式判断来输出中文。
这里其实也引申出一个原则,当你进行print,写入文件,调用系统命令(命令的参数需要中文)等系统相关的操作的时候,将字符串encode;当从文件中把内容取出来的时候,马上decode。也就是说,在程序逻辑中,操作的字符串都应该是decode过的。

OK,我们做第二个实验

#!/usr/bin/python
#-*- coding: UTF-8 -*-
import logging
logging.basicConfig(level=logging.DEBUG,
        format='%(asctime)s %(levelname)s %(message)s',
        filename='log.txt',
        filemode='a+')
x = '我爱你'
logging.info(x.decode('utf-8'))
print x.decode('utf-8')

此时print调用会报错:

1.py|11|  UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

而log调用仍然成功:

2010-06-25 17:55:05,868 INFO 我爱你

print的结果是在意料之中的,但是log的结果却是令人惊讶,猜测是log类在写入文件之前判断了内容的编码/解码,然后进行了encode之后写入。

下面是第三个,即如果组成字符串的子字符串编码/解码不一样,会怎样:

#!/usr/bin/python
#-*- coding: UTF-8 -*-
import logging
logging.basicConfig(level=logging.DEBUG,
        format='%(asctime)s %(levelname)s %(message)s',
        filename='log.txt',
        filemode='a+')
x = '我爱你'
y = '真的'
cmd = "1 %s %s" % (x,y)
print cmd
cmd = "2 %s %s" % (x.decode('utf-8'),y.decode('utf-8'))
print cmd.encode('utf-8')
cmd = "3 %s %s" % (x,y.decode('utf-8'))
print cmd

输出如下:

|| 1 我爱你 真的
|| 2 我爱你 真的
|| Traceback (most recent call last):
1.py|17|  UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 2: ordinal not in range(128)

其实第三个实验就是我在django的使用中遇到的问题,从django中用MVC模型取出的数据是经过了decode之后的,但是其他的本地变量数据却是没有经过任何decode的,导致一直报错。

OK,就这样~~

Pingbacks

Pingbacks已打开。

Trackbacks

引用地址

评论

  1. Dante

    Dante on #

    补充一下,如果这样写:
    x = '我爱你'
    y = '真的'
    z = 'z'
    cmd = "1 %s %s %s" % (x,y,z)
    print cmd
    cmd = "2 %s %s %s" % (x.decode('utf-8'),y.decode('utf-8'),z)

    则都是能正常输出的。
    对英文字符,decode和encode的结果是和原有的结果一模一样的。

    Reply

  2. fuadam

    fuadam on #

    博主你的第二个示例是有问题的。我简化你的问题如下:
    #coding: utf-8
    x = '我爱你'
    print `x` #这时不报错
    print `x.decode('utf-8')` #这时候报错
    为什么你的实验会报错,而我就不会呢.因为你的这个python脚本的文件编码格式肯定不是utf-8的,所以在做decode('utf-8')这步的时候出错了

    #coding: utf-8这句魔法注释并不是说脚本中的中文字符是utf-8编码的,而是对脚本文件的编码尝试.所以coding: utf-8可以处理cp936编码格式的文件.而反过来却不行.

    Reply

    1. Dante

      Dante on #

      呃,我可以确定我的源码文件是utf8的,要不然第三个实例也不会成功运行了。
      对于你说print 可以打印一个decode之后的结果,在于操作系统命令之间交互的时候,所有的数据都应该是encode的,所以print报错是正常的。
      我把代码改成如下:
      #!/usr/bin/python
      #-*- coding: UTF-8 -*-

      x="我爱你"
      y=x.decode('utf-8')
      print y

      报错的结果是:
      || Traceback (most recent call last):
      t.py|6| UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

      第六行,也就是print的那一行

      Reply

      1. 依云

        依云 on #

        我里也不会报错。会不会是语言设置的问题?看看`locale'命令的输出结果。

        Reply

        1. Dante

          Dante on #

          原因找到了……汗,居然是vim带来的问题。
          我在vim定义的makeprg=python -u %
          结果执行就会报错。
          直接在终端里执行 python t.py就没有问题,看样子是vim在捕获输出的时候出的问题。

          Reply

  3. fuadam

    fuadam on #

    我现在没有windows系统,周一再做实验看看。对于“在于操作系统命令之间交互的时候,所有的数据都应该是encode的”我不认同,decode以后就是unicode字符串了,这个东西是可以直接print的,不需要encode的。

    Reply

    1. Dante

      Dante on #

      我知道问题在哪里了。你我的输出方式不一样哦
      我是直接print x
      而你是print ·x·,相当于print repr(x)
      我的结果会直接打印失败,而你的结果会是
      || u'\u6211\u7231\u4f60'
      这种解码后的字符串在python程序逻辑之外实际上是没有什么用的。

      Reply

  4. TaoGOGO

    TaoGOGO on #

    我对Python的字符处理感觉不爽,gae上做东西的时候老出编码问题的错误~还是php好用哇,灭哈哈

    Reply

    1. Dante

      Dante on #

      哈哈,其实还好啦~~不过编解码确实挺令人头痛~~

      Reply

  5. fuadam

    fuadam on #

    我是故意用repr的方式的,为了看清出编码。
    #coding: utf-8
    x = ‘我爱你’
    print `x`
    这个地方不会打印unicode字符的,而是utf-8字符

    Reply

    1. Dante

      Dante on #

      呃,那如果直接print x的结果呢?

      Reply

      1. fuadam

        fuadam on #

        正常输出"我爱你",我的环境是mac osx 周一去公司用winxp实验.不知道您的测试环境是什么

        Reply

        1. Dante

          Dante on #

          我是在windows下面试的,等周一我用linux试一下,呵呵,没有mac~~

          Reply

        2. Dante

          Dante on #

          今天在linux下面试了一下,也是会报错的……

          Reply

          1. fuadam

            fuadam on #

            我在windows下测试的,没有错.
            你不是在终端中试验的吧

            Reply

            1. Dante

              Dante on #

              我想起了一个问题……
              我是python2.6
              你不会是python3.*吧……

              Reply

              1. fuadam

                fuadam on #

                家里是2.6.6,公司是2.6.5

                Reply

                1. Dante

                  Dante on #

                  晕啊,我也是2.6.5呀,怎么会完全不同的结果呢……

                  Reply

                  1. fuadam

                    fuadam on #

                    能把你的测试文件发我邮箱吗,你这个问题有点意思

                    Reply

                    1. Dante

                      Dante on #

                      汗……我想我知道原因了……
                      我是用vim写的,直接调用了vim的内置终端,结果就会报这个错误。
                      如果直接用python t.py这样的方式执行,就不会报……

                      很怪异,可能是vim的内置终端的编码不是和系统编码一致的,奇怪……

                      Reply

                      1. fuadam

                        fuadam on #

                        你是说!python % 这样执行?

                        Reply

                        1. Dante

                          Dante on #

                          不是的,是用
                          makeprg=python -u %
                          执行make

                          Reply

  6. 雨碎江南

    雨碎江南 on #

    学习了...虽然平常几乎用不着py.

    Reply

  7. Dante

    Dante on #

    print sys.getdefaultencoding()

    x = u'我爱你'
    y = unicode('我爱你','utf-8')
    z = '我爱你'.decode('utf-8')
    o = '我爱你'.decode('utf-8').encode('gbk').decode('gbk')
    print type(x),type(y),type(z),type(o)

    if x == y == z == o:
    print 'ok'

    print z.encode(sys.getfilesystemencoding())

    测试了一下,最后竟然显示了OK。。。

    Reply

发表评论