最近在使用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,就这样~~
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
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
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
依云 on #
我里也不会报错。会不会是语言设置的问题?看看`locale'命令的输出结果。
Reply
Dante on #
原因找到了……汗,居然是vim带来的问题。
我在vim定义的makeprg=python -u %
结果执行就会报错。
直接在终端里执行 python t.py就没有问题,看样子是vim在捕获输出的时候出的问题。
Reply
fuadam on #
我现在没有windows系统,周一再做实验看看。对于“在于操作系统命令之间交互的时候,所有的数据都应该是encode的”我不认同,decode以后就是unicode字符串了,这个东西是可以直接print的,不需要encode的。
Reply
Dante on #
我知道问题在哪里了。你我的输出方式不一样哦
我是直接print x
而你是print ·x·,相当于print repr(x)
我的结果会直接打印失败,而你的结果会是
|| u'\u6211\u7231\u4f60'
这种解码后的字符串在python程序逻辑之外实际上是没有什么用的。
Reply
TaoGOGO on #
我对Python的字符处理感觉不爽,gae上做东西的时候老出编码问题的错误~还是php好用哇,灭哈哈
Reply
Dante on #
哈哈,其实还好啦~~不过编解码确实挺令人头痛~~
Reply
fuadam on #
我是故意用repr的方式的,为了看清出编码。
#coding: utf-8
x = ‘我爱你’
print `x`
这个地方不会打印unicode字符的,而是utf-8字符
Reply
Dante on #
呃,那如果直接print x的结果呢?
Reply
fuadam on #
正常输出"我爱你",我的环境是mac osx 周一去公司用winxp实验.不知道您的测试环境是什么
Reply
Dante on #
我是在windows下面试的,等周一我用linux试一下,呵呵,没有mac~~
Reply
Dante on #
今天在linux下面试了一下,也是会报错的……
Reply
fuadam on #
我在windows下测试的,没有错.
你不是在终端中试验的吧
Reply
Dante on #
我想起了一个问题……
我是python2.6
你不会是python3.*吧……
Reply
fuadam on #
家里是2.6.6,公司是2.6.5
Reply
Dante on #
晕啊,我也是2.6.5呀,怎么会完全不同的结果呢……
Reply
fuadam on #
能把你的测试文件发我邮箱吗,你这个问题有点意思
Reply
Dante on #
汗……我想我知道原因了……
我是用vim写的,直接调用了vim的内置终端,结果就会报这个错误。
如果直接用python t.py这样的方式执行,就不会报……
很怪异,可能是vim的内置终端的编码不是和系统编码一致的,奇怪……
Reply
fuadam on #
你是说!python % 这样执行?
Reply
Dante on #
不是的,是用
makeprg=python -u %
执行make
Reply
雨碎江南 on #
学习了...虽然平常几乎用不着py.
Reply
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