最近在使用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,就这样~~

暂无相关产品

24则回应给“python-django的中文编码总结”

  1. Dante说道:

    补充一下,如果这样写:
    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的结果是和原有的结果一模一样的。

    [回复]

  2. fuadam说道:

    博主你的第二个示例是有问题的。我简化你的问题如下:
    #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编码格式的文件.而反过来却不行.

    [回复]

    Dante 回复:

    呃,我可以确定我的源码文件是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的那一行

    [回复]

    依云 回复:

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

    [回复]

    Dante 回复:

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

    [回复]

  3. fuadam说道:

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

    [回复]

    Dante 回复:

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

    [回复]

  4. TaoGOGO说道:

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

    [回复]

    Dante 回复:

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

    [回复]

  5. fuadam说道:

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

    [回复]

    Dante 回复:

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

    [回复]

    fuadam 回复:

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

    [回复]

    Dante 回复:

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

    [回复]

    Dante 回复:

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

    [回复]

    fuadam 回复:

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

    [回复]

    Dante 回复:

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

    [回复]

    fuadam 回复:

    家里是2.6.6,公司是2.6.5

    [回复]

    Dante 回复:

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

    [回复]

    fuadam 回复:

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

    [回复]

    Dante 回复:

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

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

    [回复]

    fuadam 回复:

    你是说!python % 这样执行?

    [回复]

    Dante 回复:

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

    [回复]

  6. 雨碎江南说道:

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

    [回复]

  7. Dante说道:

    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。。。

    [回复]

发表评论