給妹子講python-S01E08理清python字符編碼的使用方法

歡迎關注公衆號:python數據科學家php

【要點搶先看】python

1.python中編、解碼的本質是文本字符串和字節字符串的相互轉換
2.python中編、解碼方法舉例及過程解析
3.unicode、latin-一、ASCII編碼方式的兼容性問題
4.讀取二進制文件bash

上一集講清楚字符編碼的基礎概念後,我相信這一集再來介紹python中的字符編碼就會容易的多。網絡

經過上一集咱們知道,ASCII碼(包括其最多見的超集Latin-1)依賴這樣的一個假設,即每個字符與一個字節相匹配,因爲存在太多的字符,所以不可避免的會出現問題,Unicode字符集經過使用4個字節來表示1個字符,則解決了該問題。app

首先來介紹一下Python中的兩種字符串:函數

Python中有兩種字符串:文本字符串和字節字符串。其中文本字符串類型被命名爲str,內部採用Unicode字符集(兼容ASCII碼),而字節字符串則直接用來表示原始的字節序列(用print函數來打印字節字符串時,若字節在ascii碼範圍內,則顯示爲ascii碼對應的字符,其他的則直接顯示爲16進制數),該類型被命名爲bytes。ui

看一個簡單的例子:編碼

s = 'apple'
b = b'apple'
print(b)
print(type(b))
print(s)
print(type(s))

b'apple'
<class 'bytes'>
apple
<class 'str'>
複製代碼

再近距離的看看bytes類型字節字符串,本質上它就是一串單字節16進制數spa

b = b'apple'
print(b[0])
print(b[1:])
print(list(b))

97
b'pple'
[97112112108101]
複製代碼

【妹子說】那這和編碼、解碼有何關聯呢?code

從本質上來講,編碼和解碼就是str和bytes這兩種字符串類型之間的互相轉換。

str包含一個encode方法,使用特定編碼將該字符串其轉換爲一個bytes,這稱之爲編碼。bytes類包含了一個decode方法,也接受一個編碼做爲單個必要參數,並返回一個str,這稱之爲解碼。這種轉換操做是顯式的操做,且必須根據數據被編碼時採用的編碼類型進行解碼。

首先說說編碼,即將unicode的str文本字符串轉換爲bytes的字節字符串,能夠顯式的傳入指定編碼(通常來講採用utf-8編碼),或使用平臺的默認編碼。

s = 'π排球の'
b1 = s.encode('utf-8')
b2 = s.encode()
print(b1)
print(b2)

b'\xcf\x80\xe6\x8e\x92\xe7\x90\x83\xe3\x81\xae'
b'\xcf\x80\xe6\x8e\x92\xe7\x90\x83\xe3\x81\xae'
複製代碼

那麼咱們看看,在不寫編碼的時候,平臺默認的編碼方式究竟是什麼

import sys

print(sys.platform)
print(sys.getdefaultencoding())

win32
utf-8
複製代碼

能夠看出我這個平臺默認選擇的是utf-8編碼方式。

接下來咱們來比較一下unicode、latin-一、ASCII編碼方式的兼容性問題:

首先,非ASCII字符沒法使用ASCII編碼轉換成字節字符串

s = 'π排球の'
b = s.encode('ascii')

Traceback (most recent call last):
 File "E:/12homework/12homework.py", line 2in <module>
   b = s.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3:
 ordinal not in range(128)
複製代碼

其次,Latin-1和unicode編碼方式不兼容。

例如,重音字符會在latin-1字符集和unicode字符集中同時存在,可是經過latin-1和unicode編碼方式編出來的字節流是不同的,注意,雖然unicode字符集是包含了latin-1字符集,可是不表明utf-8編碼方式兼容latin-1編碼方式。由於unicode字符集中除了ascii字符集外,都是採用多字節的編碼方式,而latin-1一概採用單字節的方式

s = 'Äè'
print(s.encode('utf-8'))
print(s.encode('latin-1'))

b'\xc3\x84\xc3\xa8'
b'\xc4\xe8'
複製代碼

只有ascii字符集中的字符,三種編碼方式獲得的結果才徹底一致。對unicode進行編碼的時候,針對常規的7位ASCII文本,因爲utf-8以及latin-1編碼方式都是兼容ASCII的,因此結果都是同樣的。

s = 'abc'
print(s.encode('utf-8'))
print(s.encode('latin-1'))
print(s.encode('ascii'))

b'abc'
b'abc'
b'abc'
複製代碼

【妹子說】那對應的,再來談談decode解碼方法吧。

將bytes類型字符串轉換成str類型的unicode文本字符串也是同樣,要麼指定編碼參數,要麼使用平臺的默認參數。這個例子中,咱們要操做的字節字符串b是經過utf-8編碼方式對文本字符串'π排球の'編碼而造成的。

b = b'\xe6\x8e\x92\xe7\x90\x83'
s1 = b.decode(encoding='utf-8')
s2 = b.decode()
s3 = b.decode(encoding='latin-1')

print(s1)
print(s2)
print(s3)

排球
排球
排球
複製代碼

值得注意的是,最後一行代碼想經過latin-1解碼字節字符串,因爲字節字符串是經過utf-8編碼造成,所以這樣解碼造成獲得的只能是亂碼。

Utf-8編碼是用兩個字節來表示非ASCII的高128字符,而latin-1則是用一個字節來一一對應

【妹子說】計算機用二進制來存儲信息,而卻能在各類應用中顯示咱們須要的文字,這應該是字符編、解碼的應用吧。

很對,下面咱們來講說文本文件讀取時的編、解碼問題

當一個文件以文本模式打開的時候,被讀取的二進制存儲數據(也就是存儲的字節字符串)會自動被解碼(依據顯式提供的編碼名稱或平臺默認的編碼名稱),而且將其返回爲一個str。寫入文件時,會接受一個str,而且將其傳輸到文件以前自動編碼成字節字符串以供磁盤存儲。

當一個文件以二進制模式打開時,須要在open方法的模式字符串參數裏添加一個b,此時讀取的數據不會以任何方式解碼,而是直接返回其原始內容,即一個bytes對象;寫入文件時,接受一個bytes對象,而且將其傳送到文件中且不進行修改。

在讀取文本文件的時候,若是open函數沒有聲明他們如何編碼,python3會因其所運行的系統而選取默認的編碼方式,默認狀況下,python3 指望文件使用 utf-8進行編碼。但因爲文件並不老是在同一個系統中被保存和打開,所以會帶來亂碼的風險,因此咱們須要顯式的指定編碼。

補充的說明一下,能夠很簡單的進行一個分類:處理圖像文件、設備數據流等,可使用bytes和二進制模式文件處理;而若是要處理的內容實質是文本的內容,例如程序輸出、HTML、國際化文本或CSV或XML文件,則可能要使用str和文本模式文件

例如,咱們先把AÄBèC用UTF-8編碼後存入utf-8data文件,再來讀取他,具體看看這裏是如何實現的。

s = 'AÄBèC'

with open('utf-8data','w',encoding='utf-8'as f:
   f.write(s)

with open('utf-8data','r',encoding='utf-8'as f:
   u_str = f.read()
print(u_str)


AÄBèC
複製代碼

這裏用到的文件讀寫的方法後面的章節會詳細介紹,如今知道他是什麼就行了。

以二進制的形式讀取文件。

還有一種咱們以前介紹過的用法,文本字符串在存儲到磁盤的時候會編碼成字節字符,所以咱們也能夠先以字節字符串的形式從文件中將其讀取,而後再進行解碼。

這樣作的緣由有二,一種是所接收的多是非文本數據,如一個圖像文件;另外一個潛在緣由是沒法肯定所讀取文本文件的編碼,可能須要依據其餘信息再肯定:

with open('utf-8data''rb'as f:
   byte_str = f.read()

print(byte_str)
print(byte_str.decode(encoding='utf-8'))

b'A\xc3\x84B\xc3\xa8C'
AÄBèC
複製代碼

字符串編、解碼在python中很重要,特別是在網絡爬蟲等網絡應用程序中,在後面的實際應用中會感覺到他的做用會愈來愈明顯。

【妹子說】這一集的內容不少,細緻剖析了python中的兩種字符串類型和編、解碼的處理方法。再結合以前的三集,就能從基本使用、字符編、解碼的維度閉環出一個完整的知識網絡了,收穫很大。

公衆號二維碼:python數據科學家:

相關文章
相關標籤/搜索