[Python]输出中文的方法,搞懂编码encode和解码decode
字符串编码解码这些知识我有了解一些,因为刚接触python,这些东西在python中的表现就有点陌生了,这里记录下遇到的相关实例供学习参考。
(字符集和字符编码的相关知识可以参考该文章,讲的很彻底:http://cenalulu.github.io/linux/character-encoding/)
问题1
首先,当我们编辑py脚本时,如果直接输出中文,会报错:
#!/usr/bin/python
print '你好'
'''
SyntaxError: Non-ASCII character '\xe4' in file xxx, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
'''
可以看到,错误信息:非ASCII码出现,但是没有编码声明,编译器不识别。
这里,添加一段声明即可实现:
#-*- coding: utf-8
这是什么原因呢? 我们都知道,计算机识别的语言都是0/1二进制格式,我们需要通过进行字符编码转换处理后再丢给计算机让其能识别我们的输入内容,因此才会报错没有编码声明。
在python 中,对于str,选用Unicode作为其默认编码字符集,utf-8就是对unicode进行编码的一种方式。
这个引用的意思就是说表明该文件的编码类型是utf-8编码类型。
-
当取回来的数据与你当前脚本中声明的编码不一致时就要做编码转换
-
在编码转换时首先要将该数据以自身编码的格式换成unicode码,再将这个unicode按utf8编码
Python 里面的编码和解码也就是 unicode 和 str 这两种形式的相互转化。编码encode是 unicode -> str,解码decode就是 str -> unicode。
s = '你好啊'
print type(s.decode('utf-8'))
print type(s.decode('utf-8').encode('utf-8'))
'''
<type 'unicode'>
<type 'str'>
'''
python解析器需通过声明utf8编码类型,之后对其进行先编码后解码转成unicode。整个过程如下:
计算机 encode decode
CLI: str --- > str(Unicode)
再举个例子解释下上面说的内容,下面会报错:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
import sys
print sys.getdefaultencoding() #ascii
u = '你好'
str1 = u.encode('utf-8')
print str1
# 等价于
s = '你好 中文'
s.decode('ascii').encode('utf-8')
所以上面在做什么呢?将str u重新编码为utf-8 的格式,即进行 unicode -> str 的转换。
这里提一下,不同编码类型的转换是通过unicode作为中间层,也就是说,编码1-->unicode-->编码2
因为 u 本身就是 str 类型的,因此 Python 会自动先将 u 解码为 unicode ,然后再编码成 utf-8。
我们没有指明解码方式的情况下,python 就会使用 默认的sys.defaultencoding 方式来解码,如果你不更改sys的编码方式,那么它就是ascii,如果 str u 不是这个类型就会出错(u是utf-8类型的)。
来看下fix的方法:
方法一. 将系统sys的编码方式转成utf8,让解码和编码的方式相同
import sys
# Python2.5 初始化后会删除 sys.setdefaultencoding 这个方法,我们需要重新载入
if sys.version_info[0] == 3:
from importlib import reload
reload(sys)
if sys.version_info[0] == 2:
reload(sys)
sys.setdefaultencoding('utf-8')
u = '你好'
str1 = u.encode('utf-8')
print str1
方法二.先解码后编码,保证编码解码对象类型一致
import sys
print sys.getdefaultencoding() #ascii
u = '你好'
str1 = u.decode('utf-8').encode('utf-8')
print str1
问题2
当我们创建一个list 或者dict的时候,如果带有中文,则会输出bytes相关的内容,这是为什么呢?
s = ['你好','hello']
print s
#['\xe4\xbd\xa0\xe5\xa5\xbd', 'hello']
个人的理解,list or dict不同于str(str本身已经定义映射了unicode对应编码的内容),内部元素 '你好'本应该是编码后对应编码字符集的内容,而在计算中经过数据编码和解码会让其变成bytes流,也就是处理后表示的数据。如果不把它当str来处理的话(也就是编码处理),对于整体而言,它输出的就是bytes(未经过编码处理的数据,也就是计算机经过解码后内存显示的数据).
>>> s = '你好'
>>> print s
你好
>>> s
'\xe4\xbd\xa0\xe5\xa5\xbd'
解决方法(list or dic 通用):
方法一. 安装pip install uniout,文件import uniout即可显示
s = ['你好','hello']
print s
import uniout
print s
#['\xe4\xbd\xa0\xe5\xa5\xbd', 'hello']
#['你好', 'hello']
方法二.使用decode('string_escape')
s = ['你好','hello']
print s
print str(s).decode('string_escape')
#['\xe4\xbd\xa0\xe5\xa5\xbd', 'hello']
#['你好', 'hello']
方法三.导入json模块,转str指定编码类型
import json
s = ['你好','hello']
print s
s = json.dumps(s, encoding="utf-8", ensure_ascii=False)
print s
print json.loads(s)
'''
['\xe4\xbd\xa0\xe5\xa5\xbd', 'hello']
["你好", "hello"]
[u'\u4f60\u597d', u'hello']
'''
问题3
当你拿到的数据是unicode格式并且带有中文的时候(在python2.x中,unicode字符串需要在字符串前加u来表示,而在python3.x中,unicode字符串已经是默认格式,因此不再需要加u)。
Case1(Str):
下面的三种方法均可
s ='\u7b14\u8bb0'
print s.decode('unicode-escape')
print s.encode().decode('unicode_escape')
print unicode(s, 'unicode_escape')
print eval('u"%s"' %s)
'''
\u7b14\u8bb0
笔记
笔记
笔记
笔记
'''
Case2(list or dict):
a=[u'\u4f60\u597d',u'\u4e16\u754c']
print a,type(a[0])
b=[u'\u4f60\u597d']
print str(b).decode('unicode-escape')
Case3(包含u')
需要替换u\为\,否则输出会包含u
list1 = [{'channel_id': -3, 'name1': u'\u7ea2\u5fc3\u5146\u8d6b','name2': u'\u79c1\u4eba\u5146\u8d6b'}]
print str(list1).decode('unicode-escape')
s = str(list1).replace('u\'','\'')
print s.decode('unicode-escape')
Tips:
- 判断字符串的编码
isinstance(s, str) 用来判断是否为一般字符串
isinstance(s, unicode) 用来判断是否为unicode
- Unicode和str之间的转换
s = '你好啊'
print type(s.decode('utf-8')),s.decode('utf-8')
print type(s.decode('utf-8').encode('utf-8'))
print 'unicode: ' + s.decode('utf-8').encode('utf-8')
print 'unicode: ' + s.decode('utf-8').encode('unicode-escape').decode('utf-8')
'''
<type 'unicode'> 你好啊
<type 'str'>
unicode: 你好啊
unicode: \u4f60\u597d\u554a
'''
s = '\u4f60\u597d\u554a'
s1 = '你好啊'
s1 = s1.decode('utf-8').encode('unicode-escape').decode('utf-8')
s2 = 'abc'
print s,type(s)
print s1,type(s1)
print s2,type(s2)
'''
\u4f60\u597d\u554a <type 'str'>
\u4f60\u597d\u554a <type 'unicode'>
abc <type 'str'>
'''
def filter_unicode(u):
if type(u).__name__!="unicode":
u = u.decode('utf-8').encode('utf-8')
return unicode(u,"utf-8").decode('unicode-escape')
else:
return u.encode('utf-8').decode('unicode_escape')
print filter_unicode(s),type(filter_unicode(s))
print filter_unicode(s1),type(filter_unicode(s1))
print filter_unicode(s2),type(filter_unicode(s2))
'''
你好啊 <type 'unicode'>
你好啊 <type 'unicode'>
abc <type 'unicode'>
'''