字符串編碼解碼這些知識我有了解一些,因為剛接觸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'>
'''