Python: 熟悉又陌生的字元編碼
- 基本概念
- 常見字元編碼簡介
- Python 的預設編碼
- Python2 中的字元型別
- UnicodeEncodeError & UnicodeDecodeError 根源
基本概念
- 字元(Character)
在電腦和電信領域中,字元是一個資訊單位,它是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。比如,一個漢字,一個英文字母,一個標點符號等都是一個字元。
- 字符集(Character set)
字符集是字元的集合。字符集的種類較多,每個字符集包含的字元個數也不同。比如,常見的字符集有 ASCII 字符集、GB2312 字符集、Unicode 字符集等,其中,ASCII 字符集共有 128 個字元,包含可顯示字元(比如英文大小寫字元、阿拉伯數字)和控制字元(比如空格鍵、回車鍵);GB2312 字符集是中國國家標準的簡體中文字符集,包含簡化漢字、一般符號、數字等;Unicode 字符集則包含了世界各國語言中使用到的所有字元,
- 字元編碼(Character encoding)
字元編碼,是指對於字符集中的字元,將其編碼為特定的二進位制數,以便計算機處理。常見的字元編碼有 ASCII 編碼,UTF-8 編碼,GBK 編碼等。一般而言,字符集和字元編碼往往被認為是同義的概念,比如,對於字符集 ASCII,它除了有「字元的集合」這層含義外,同時也包含了「編碼」的含義,也就是說,ASCII 既表示了字符集也表示了對應的字元編碼。
下面我們用一個表格做下總結:
概念 | 概念描述 | 舉例 |
---|---|---|
字元 | 一個資訊單位,各種文字和符號的總稱 | ‘中’, ‘a’, ‘1’, ‘$’, ‘¥’, … |
字符集 | 字元的集合 | ASCII 字符集, GB2312 字符集, Unicode 字符集 |
字元編碼 | 將字符集中的字元,編碼為特定的二進位制數 | ASCII 編碼,GB2312 編碼,Unicode 編碼 |
位元組 | 計算機中儲存資料的單元,一個 8 位(bit)的二進位制數 | 0x01, 0x45, … |
常見字元編碼簡介
常見的字元編碼有 ASCII 編碼,GBK 編碼,Unicode 編碼和 UTF-8 編碼等等。這裡,我們主要介紹 ASCII、Unicode 和 UTF-8。
ASCII
計算機是在美國誕生的,人家用的是英語,而在英語的世界裡,不過就是英文字母,數字和一些普通符號的組合而已。
在 20 世紀 60 年代,美國製定了一套字元編碼方案,規定了英文字母,數字和一些普通符號跟二進位制的轉換關係,被稱為 ASCII (American Standard Code for Information Interchange,美國資訊互換標準編碼) 碼。
比如,大寫英文字母 A 的二進位制表示是 01000001(十進位制 65),小寫英文字母 a 的二進位制表示是 01100001 (十進位制 97),空格 SPACE 的二進位制表示是 00100000(十進位制 32)。
Unicode
ASCII 碼只規定了 128 個字元的編碼,這在美國是夠用的。可是,計算機後來傳到了歐洲,亞洲,乃至世界各地,而世界各國的語言幾乎是完全不一樣的,用 ASCII 碼來表示其他語言是遠遠不夠的,所以,不同的國家和地區又制定了自己的編碼方案,比如中國大陸的 GB2312 編碼 和 GBK 編碼等,日本的 Shift_JIS 編碼等等。
雖然各個國家和地區可以制定自己的編碼方案,但不同國家和地區的計算機在資料傳輸的過程中就會出現各種各樣的亂碼(mojibake),這無疑是個災難。
怎麼辦?想法也很簡單,就是將全世界所有的語言統一成一套編碼方案,這套編碼方案就叫 Unicode,它為每種語言的每個字元設定了獨一無二的二進位制編碼,這樣就可以跨語言,跨平臺進行文字處理了,是不是很棒!
Unicode 1.0 版誕生於 1991 年 10 月,至今它仍在不斷增修,每個新版本都會加入更多新的字元,目前最新的版本為 2016 年 6 月 21 日公佈的 9.0.0。
Unicode 標準使用十六進位制數字,而且在數字前面加上字首 U+
,比如,大寫字母「A」的 unicode 編碼為 U+0041
,漢字「嚴」的 unicode 編碼為 U+4E25
。更多的符號對應表,可以查詢 unicode.org,或者專門的漢字對應表。
UTF-8
Unicode 看起來已經很完美了,實現了大一統。但是,Unicode 卻存在一個很大的問題:資源浪費。
為什麼這麼說呢?原來,Unicode 為了能表示世界各國所有文字,一開始用兩個位元組,後來發現兩個位元組不夠用,又用了四個位元組。比如,漢字「嚴」的 unicode 編碼是十六進位制數 4E25
,轉換成二進位制有十五位,即 100111000100101,因此至少需要兩個位元組才能表示這個漢字,但是對於其他的字元,就可能需要三個或四個位元組,甚至更多。
這時,問題就來了,如果以前的 ASCII 字符集也用這種方式來表示,那豈不是很浪費儲存空間。比如,大寫字母「A」的二進位制編碼為 01000001,它只需要一個位元組就夠了,如果 unicode 統一使用三個位元組或四個位元組來表示字元,那「A」的二進位制編碼的前面幾個位元組就都是 0
,這是很浪費儲存空間的。
為了解決這個問題,在 Unicode 的基礎上,人們實現了 UTF-16, UTF-32 和 UTF-8。下面只說一下 UTF-8。
UTF-8 (8-bit Unicode Transformation Format) 是一種針對 Unicode 的可變長度字元編碼,它使用一到四個位元組來表示字元,例如,ASCII 字元繼續使用一個位元組編碼,阿拉伯文、希臘文等使用兩個位元組編碼,常用漢字使用三個位元組編碼,等等。
因此,我們說,UTF-8 是 Unicode 的實現方式之一,其他實現方式還包括 UTF-16(字元用兩個或四個位元組表示)和 UTF-32(字元用四個位元組表示)。
Python 的預設編碼
Python2 的預設編碼是 ascii,Python3 的預設編碼是 utf-8,可以通過下面的方式獲取:
- Python2
Python123456 Python2.7.11(default,Feb242016,10:48:05)[GCC4.2.1Compatible Apple LLVM7.0.2(clang-700.1.81)]on darwinType"help","copyright","credits"or"license"formore information.>>>importsys>>>sys.getdefaultencoding()'ascii'
- Python3
Python123456 Python3.5.2(default,Jun292016,13:43:58)[GCC4.2.1Compatible Apple LLVM7.3.0(clang-703.0.31)]on darwinType"help","copyright","credits"or"license"formore information.>>>importsys>>>sys.getdefaultencoding()'utf-8'
Python2 中的字元型別
Python2 中有兩種和字串相關的型別:str 和 unicode,它們的父類是 basestring。其中,str 型別的字串有多種編碼方式,預設是 ascii,還有 gbk,utf-8 等,unicode 型別的字串使用 u'...'
的形式來表示,下面的圖展示了 str 和 unicode 之間的關係:
兩種字串的相互轉換概括如下:
- 把 UTF-8 編碼表示的字串 ‘xxx’ 轉換為 Unicode 字串 u’xxx’ 用
decode('utf-8')
方法:
Python12 >>>'中文'.decode('utf-8')u'\u4e2d\u6587'
- 把 u’xxx’ 轉換為 UTF-8 編碼的 ‘xxx’ 用
encode('utf-8')
方法:
Python12 >>>u'中文'.encode('utf-8')'\xe4\xb8\xad\xe6\x96\x87'
UnicodeEncodeError & UnicodeDecodeError 根源
用 Python2 編寫程式的時候經常會遇到 UnicodeEncodeError 和 UnicodeDecodeError,它們出現的根源就是如果程式碼裡面混合使用了 str 型別和 unicode 型別的字串,Python 會預設使用 ascii 編碼嘗試對 unicode 型別的字串編碼 (encode),或對 str 型別的字串解碼 (decode),這時就很可能出現上述錯誤。
下面有兩個常見的場景,我們最好牢牢記住:
- 在進行同時包含 str 型別和 unicode 型別的字串操作時,Python2 一律都把 str 解碼(decode)成 unicode 再運算,這時就很容易出現 UnicodeDecodeError。
讓我們看看例子:
Python123456 | >>>s='你好'# str 型別, utf-8 編碼>>>u=u'世界'# unicode 型別>>>s+u# 會進行隱式轉換,即 s.decode('ascii') + uTraceback(most recent call last):File"<stdin>",line1,in<module>UnicodeDecodeError:'ascii'codec can'tdecode byte0xe4inposition0:ordinal notinrange(128) |
為了避免出錯,我們就需要顯示指定使用 ‘utf-8’ 進行解碼,如下:
Python12345 | >>>s='你好'# str 型別,utf-8 編碼>>>u=u'世界'>>>>>>s.decode('utf-8')+u# 顯示指定 'utf-8' 進行轉換u'\u4f60\u597d\u4e16\u754c'# 注意這不是錯誤,這是 unicode 字串 |
- 如果函式或類等物件接收的是 str 型別的字串,但你傳的是 unicode,Python2 會預設使用 ascii 將其編碼成 str 型別再運算,這時就很容易出現 UnicodeEncodeError。
讓我們看看例子:
Python12345 | >>>u_str=u'你好'>>>str(u_str)Traceback(most recent call last):File"<stdin>",line1,in<module>UnicodeEncodeError:'ascii'codec can'tencode characters inposition0-1:ordinal notinrange(128) |
在上面的程式碼中,u_str 是一個 unicode 型別的字串,由於 str()
的引數只能是 str 型別,此時 Python 會試圖使用 ascii 將其編碼成 ascii,也就是:
1 | u_str.encode('ascii')//u_str是unicode字串 |
上面將 unicode 型別的中文使用 ascii 編碼轉,肯定會出錯。
再看一個使用 raw_input 的例子,注意 raw_input 只接收 str 型別的字串:
Python1234567891011121314151617181920212223242526 | >>>name=raw_input('input your name: ')inputyour name:ethan>>>name'ethan'>>>name=raw_input('輸入你的姓名:')輸入你的姓名:小明>>>name'\xe5\xb0\x8f\xe6\x98\x8e'>>>type(name)<type'str'>>>>name=raw_input(u'輸入你的姓名: ')# 會試圖使用 u'輸入你的姓名'.encode('ascii')Traceback(most recent call last):File"<stdin>",line1,in<module>UnicodeEncodeError:'ascii'codec can't encode characters in position 0-5: ordinal not in range(128)>>> name = raw_input(u'輸入你的姓名:'.encode('utf-8')) #可以,但此時 name 不是 unicode 型別輸入你的姓名: 小明>>> name'\xe5\xb0\x8f\xe6\x98\x8e'>>> type(name)<type 'str'>>>> name = raw_input(u'輸入你的姓名:'.encode('utf-8')).decode('utf-8') # 推薦輸入你的姓名: 小明>>> nameu'\u5c0f\u660e'>>> type(name)<type 'unicode'> |
再看一個重定向的例子:
Python12 | hello=u'你好'printhello |
將上面的程式碼儲存到檔案 hello.py
,在終端執行 python hello.py
可以正常列印,但是如果將其重定向到檔案 python hello.py > result
會發現 UnicodeEncodeError
。
這是因為:輸出到控制檯時,print 使用的是控制檯的預設編碼,而重定向到檔案時,print 就不知道使用什麼編碼了,於是就使用了預設編碼 ascii 導致出現編碼錯誤。
應該改成如下:
Python12 | hello=u'你好'printhello.encode('utf-8') |
這樣執行 python hello.py > result
就沒有問題。
小結
- UTF-8 是一種針對 Unicode 的可變長度字元編碼,它是 Unicode 的實現方式之一。
- Unicode 字符集有多種編碼標準,比如 UTF-8, UTF-7, UTF-16。
- 在進行同時包含 str 型別和 unicode 型別的字串操作時,Python2 一律都把 str 解碼(decode)成 unicode 再運算。
- 如果函式或類等物件接收的是 str 型別的字串,但你傳的是 unicode,Python2 會預設使用 ascii 將其編碼成 str 型別再運算。
參考資料
相關推薦
Python: 熟悉又陌生的字元編碼
基本概念 常見字元編碼簡介 Python 的預設編碼 Python2 中的字元型別 UnicodeEncodeError & UnicodeDecodeError 根源 基本概念 字元(Character) 在電腦和電信領域中,字元是一個資訊單位,它是各種文字和符號的總稱,包括各國家文字、標
那些熟悉又陌生的 css2、css3 樣式,持續復習
所有 com visible absolut value 其余 分頁 排版 彈性 initial關鍵字: 除了 Internet Explorer,其他的主流瀏覽器都支持 initial 關鍵字。 Opera 15 之前的版本不支持 ini
第二篇 Python資料型別、字元編碼、檔案處理
一、引子 1、什麼是資料? x=10,10是我們要儲存的資料 2、為何資料要分不同的型別
熟悉又陌生的跨域訪問與CORS
說到跨域訪問,是既熟悉又陌生,熟悉是因為只要做過web專案,尤其是前後端分離的專案,都碰到過ajax跨域訪問的麻煩,跨域訪問就如字面意思,只要協議、域名、埠有任何一個不同,都被當作是不同的域。對於跨域訪問,是有限制的;陌生的是,很多跨域無法訪問的問題只能一味網上找解決方案,而不知道跨域乃
趣談網路協議(二)ifconfig:最熟悉又陌生的命令列
ip addr [email protected]:~# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/lo
python爬蟲自動解決字元編碼問題
In [34]: import requests ...: ...: response = requests.get('http://www.dytt8.net/index.htm') ...: print(response.text[2
python全棧_008_Python3 字元編碼
1:字元編碼 我們已經講過了,字串也是一種資料型別,但是,字串比較特殊的是還有一個編碼問題。 因為計算機只能處理數字,如果要處理文字,就必須先把文字轉換為數字才能處理。最早的計算機在設計時採用8個位元(bit)作為一個位元組(byte),所以,一個位元組能表示的最大的整數就是255(二進位制111
Python基礎知識之字元編碼和轉碼
字元編碼 python直譯器在載入.py檔案中的程式碼時,會對內容進行編碼(預設ASCII) ASCII碼 ASCII(American Standard Code for Information Interchange,美國標準資訊交換程式碼)是基於拉丁字母的一套電腦編碼系
【網路協議】筆記三 | ifconfig:最熟悉又陌生的命令列
知識點 檢視IP地址 IP地址五大類 CIDR 私有與公有地址 怎麼檢視IP地址? 通常Windows是ipconfig,linux是ifconfig,而linux還可以使用ip addr檢視 例如執行ip addr root@te
熟悉又陌生的HTTPS
歡迎大家加入QQ群一起討論: 489873144(android格調小窩) 全文較長,主要從概覽,到原理,到抓包整個流程進行全方位的解讀,個人能力有限,我也是通過在網上尋找資料,結合自己的理解整理出來的,如有不當,歡迎指正。 如果你認真看完本文,以下幾個
python基礎學習之字元編碼unicode、decode、encode的相互轉化
#unicode # encode :解碼 decode:編碼 一、encode的使用 s="你好" s_gbk = s.encode("gbk") print(s) print(s.encode()) #預設utf-8,進行enc
python基礎--字元編碼
一 、什麼是編碼? 基本概念很簡單。首先,我們從一段資訊即訊息說起,訊息以人類可以理解、易懂的表示存在。我打算將這種表示稱為“明文”(plain text)。對於說英語的人,紙張上列印的或螢幕上顯示的英文單詞都算作明文。 其次,我們需要能將明文表示的訊息轉成另外某種表示,我們還需要能將編碼文
python開發基礎之資料型別、字元編碼、檔案操作
一、知識點 1.身份運算: 2.現在計算機系統通用的字元編碼工作方式:在計算機記憶體中,統一使用Unicode編碼,當需要儲存到硬碟或者需要傳輸的時候,就轉換為UTF-8編碼。用記事本編輯的時候,從檔案讀取的UTF-8字元被轉換為Unicode字元到記憶體裡,編輯完成後,儲存的時候再把Unicode轉
python基礎 字元編碼
一.計算機基礎知識 應用程式任何操作硬體的的請求,都需要向作業系統發起呼叫,然後由作業系統去呼叫; cpu----記憶體-----硬碟 二.文字編輯器存取檔案的原理 1.開啟編輯器就打開了一個啟動的進行,是在記憶體中,所以編輯器編寫的內容也都是存放於記憶體中的,斷電後資料丟
python字元編碼和檔案處理
一.瞭解字元編碼的知識儲備 1.文字編輯器存取檔案的原理(nodepad++,python,word) 開啟編輯器就打開了啟動了一個程序,是在記憶體中的,所以,用編輯器編寫的內容也都是存放於記憶體中的,斷電後資料丟失 要想永遠儲存,需要點選儲存按鈕:編輯器
(三)Python基礎2:瞭解字元編碼
2-str-code.py # 瞭解字元編碼 # 字串也是一種資料型別,但是,字串比較特殊的是還有一個編碼問題。 # 因為計算機只能處理數字,如果要處理文字,就必須先把文字轉換為數字才能處理。 # 最早的計算機在設計時採用8個位元(bit)作為一個位元組(byte),所以,一個位元
Python學習【1.1.2】-字元編碼
字元編碼 A)文字與直譯器 1)文字編輯器存取檔案的原理(nodepad++/pycharm/word等) 開啟編輯器就打開了啟動了一個程序,是在記憶體中,所以在編輯器編寫的內容也是存放在記憶體中,斷電後資料丟失,所以需要儲存到硬碟上,點選儲存按鈕之後,就從記憶體中把資料刷到
python學習day9 字元編碼和檔案處理
1.字元編碼 x='上' #unicode的二進位制---------》編碼--------》gbk格式的二進位制 res=x.encode('gbk') #bytes 位元組型別 print(res,type(res)) m=res.decode('gbk') print(m)&nb
python語法_字元編碼
二進位制: ascll:只能存英文和拉聽字元,一個字元佔一個位元組,8位 gb2312:只能存6700多箇中文,1980年 gbk1.0:能存2萬多字元,1995年 gbk18030:2000 27000萬字符 unicode:統一各個國家的編碼,萬國碼。每個位元組佔四個位元組, 最初
深入理解python字元編碼(包含2.x與3.x)
2018-11-29 09:44:30 引子 計算機要想工作必須通電,即用‘電’驅使計算機幹活,也就是說‘電’的特性決定了計算機的特性。 電的特性即高低電平(人類從邏輯上將二進位制數1對應高電平,二進位制數0對應低電平), 關於磁碟的磁特性也是同樣的道理。