鍍金池/ 教程/ Python/ 字符編碼
標準庫 (4)
如何成為 Python 高手
標準庫 (6)
標準庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準備
函數(shù)練習
標準庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標準庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實戰(zhàn)
運算符
類(3)
字典(2)
語句(1)
數(shù)和四則運算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標準庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學函數(shù)和運算優(yōu)先級
字符串(3)
為計算做準備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標準庫 (2)
列表(2)
元組

字符編碼

我在第一版的《零基礎學 Python》中,這個標題前面加了“坑爹”兩個字。在后來的實踐中,很多朋友都在網(wǎng)上問我關于編碼的事情。說明這的確是一個“坑”。

首先說明,在 Python2 中,編碼問題的確有點麻煩。但是,Python3 就不用糾結(jié)于此了。但是,正如前面所說的原因,至少本教程還是用 Python2,所以,必須要搞清楚編碼。當然了,搞清楚,也不是壞事。

字符編碼,在編程中,是一個讓學習者比較郁悶的東西,比如一個 str,如果都是英文,好說多了。但恰恰不是如此,中文是我們不得不用的。所以,哪怕是初學者,都要了解并能夠解決字符編碼問題。

>>> name = '老齊'
>>> name
'\xe8\x80\x81\xe9\xbd\x90'

在你的編程中,你遇到過上面的情形嗎?認識最下面一行打印出來的東西嗎?看人家英文,就好多了

>>> name = "qiwsir"
>>> name
'qiwsir'

難道這是中文的錯嗎?看來投胎真的是一個技術活。是的,投胎是技術活,但上面的問題不是中文的錯。

編碼

什么是編碼?這是一個比較玄乎的問題。也不好下一個普通定義。我看到有的教材中有定義,不敢說他的定義不對,至少可以說不容易理解。

古代打仗,擊鼓進攻、鳴金收兵,這就是編碼。把要傳達給士兵的命令對應為一定的其它形式,比如命令“進攻”,經(jīng)過如此的信息傳遞:

http://wiki.jikexueyuan.com/project/start-learning-python/images/11001.png" alt="" />

  1. 長官下達進攻命令,傳令員將這個命令編碼為鼓聲(如果復雜點,是不是有幾聲鼓響,如何進攻呢?)。
  2. 鼓聲在空氣中傳播,比傳令員的嗓子吼出來的聲音傳播的更遠,士兵聽到后也不會引起歧義,一般不會有士兵把鼓聲當做打呼嚕的聲音。這就是“進攻”命令被編碼成鼓聲之后的優(yōu)勢所在。
  3. 士兵聽到鼓聲,就是接收到信息之后,如果接受過訓練或者有人告訴過他們,他們就知道這是讓我進攻。這個過程就是解碼。所以,編碼方案要有兩套。一套在信息發(fā)出者那里,另外一套在信息接受者這里。經(jīng)過解碼之后,士兵明白了,才行動。

以上過程比較簡單。其實,真實的編碼和解碼過程,要復雜了。不過,原理都差不多的。

舉一個似乎遙遠,其實不久前人們都在使用的東西做例子:電報

電報是通信業(yè)務的一種,在 19 世紀初發(fā)明,是最早使用電進行通信的方法。電報大為加快了消息的流通,是工業(yè)社會的其中一項重要發(fā)明。早期的電報只能在陸地上通訊,后來使用了海底電纜,開展了越洋服務。到了 20 世紀初,開始使用無線電撥發(fā)電報,電報業(yè)務基本上已能抵達地球上大部份地區(qū)。電報主要是用作傳遞文字訊息,使用電報技術用作傳送圖片稱為傳真。

中國首條出現(xiàn)電報線路是 1871 年,由英國、俄國及丹麥敷設,從香港經(jīng)上海至日本長崎的海底電纜。由于清政府的反對,電纜被禁止在上海登陸。后來丹麥公司不理清政府的禁令,將線路引至上海公共租界,并在 6 月 3 日起開始收發(fā)電報。至于首條自主敷設的線路,是由福建巡撫丁日昌在臺灣所建,1877 年 10 月完工,連接臺南及高雄。1879 年,北洋大臣李鴻章在天津、大沽及北塘之間架設電報線路,用作軍事通訊。1880 年,李鴻章奏準開辦電報總局,由盛宣懷任總辦。并在 1881 年 12 月開通天津至上海的電報服務。李鴻章説:“五年來,我國創(chuàng)設沿江沿海各省電線,總計一萬多里,國家所費無多,巨款來自民間。當時正值法人挑釁,將帥報告軍情,朝廷傳達指示,均相機而動,無絲毫阻礙。中國自古用兵,從未如此神速。出使大臣往來問答,朝發(fā)夕至,相隔萬里好似同居庭院。舉設電報一舉三得,既防止外敵侵略,又加強國防,亦有利于商務?!碧旖蚬匐娋钟诟釉鈦y全毀。1887 年,臺灣巡撫劉銘傳敷設了福州至臺灣的海底電纜,是中國首條海底電纜。1884 年,北京電報開始建設,采用"安設雙線,由通州展至京城,以一端引入署中,專遞官信,以一端擇地安置用便商民",同年 8 月 5 日,電報線路開始建設,所有電線桿一律漆成紅色。8 月 22 日,位于北京崇文門外大街西的喜鵲胡同的外城商用電報局開業(yè)。同年 8 月 30 日,位于崇文門內(nèi)泡子和以西的呂公堂開局,專門收發(fā)官方電報。

為了傳達漢字,電報部門準備由 4 位數(shù)字或 3 位羅馬字構(gòu)成的代碼,即中文電碼,采用發(fā)送前將漢字改寫成電碼發(fā)出,收電報后再將電碼改寫成漢字的方法。

列位看官注意了,這里出現(xiàn)了電報中用的“中文電碼”,這就是一種編碼,將漢字對應成阿拉伯數(shù)字,從而能夠用電報發(fā)送漢字。

1873 年,法國駐華人員威基杰參照《康熙字典》的部首排列方法,挑選了常用漢字 6800 多個,編成了第一部漢字電碼本《電報新書》。

電報中的編碼被稱為摩爾斯電碼,英文是 Morse Code

摩爾斯電碼(英語:Morse Code)是一種時通時斷的信號代碼,通過不同的排列順序來表達不同的英文字母、數(shù)字和標點符號。是由美國人薩繆爾·摩爾斯在 1836 年發(fā)明。

摩爾斯電碼是一種早期的數(shù)字化通信形式,但是它不同于現(xiàn)代只使用 0 和 1 兩種狀態(tài)的二進制代碼,它的代碼包括五種:點(.)、劃(-)、每個字符間短的停頓(在點和劃之間的停頓)、每個詞之間中等的停頓、以及句子之間長的停頓

看來電報員是一個技術活,不同長短的停頓都代表了不同意思。哦,對了,有一個老片子《永不消逝的電波》,看完之后保證你才知道,里面根本就沒有講電報是怎么編碼的。

摩爾斯電碼在海事通訊中被作為國際標準一直使用到 1999 年。1997 年,當法國海軍停止使用摩爾斯電碼時,發(fā)送的最后一條消息是:“所有人注意,這是我們在永遠沉寂之前最后的一聲吶喊!”

http://wiki.jikexueyuan.com/project/start-learning-python/images/11002.png" alt="" />

我瞪著眼看了老長時間,這兩行不是一樣的嗎?

不管這個了,總之,這就是編碼。

計算機中的字符編碼

先抄一段維基百科對字符編碼的解釋:

字符編碼(英語:Character encoding)、字集碼是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式、自然數(shù)串行、8 位組或者電脈沖),以便文本在計算機中存儲和通過通信網(wǎng)絡的傳遞。常見的例子包括將拉丁字母表編碼成摩斯電碼和 ASCII。其中,ASCII 將字母、數(shù)字和其它符號編號,并用 7 比特的二進制來表示這個整數(shù)。通常會額外使用一個擴充的比特,以便于以 1 個字節(jié)的方式存儲。

在計算機技術發(fā)展的早期,如 ASCII(1963 年)和 EBCDIC(1964 年)這樣的字符集逐漸成為標準。但這些字符集的局限很快就變得明顯,于是人們開發(fā)了許多方法來擴展它們。對于支持包括東亞 CJK 字符家族在內(nèi)的寫作系統(tǒng)的要求能支持更大量的字符,并且需要一種系統(tǒng)而不是臨時的方法實現(xiàn)這些字符的編碼。

在這個世界上,有好多不同的字符編碼。但是,它們不是自己隨便搞搞的。而是要有一定的基礎,往往是以名叫 ASCII 的編碼為基礎,這里邊也應該包括北朝鮮吧(不知道他們用什么字符編碼,瞎想的,別當真,不代表本教材立場,只代表瞎想)。

ASCII(pronunciation: 英語發(fā)音:/??ski/ ASS-kee[1],American Standard Code for Information Interchange,美國信息交換標準代碼)是基于拉丁字母的一套電腦編碼系統(tǒng)。它主要用于顯示現(xiàn)代英語,而其擴展版本 EASCII 則可以部分支持其他西歐語言,并等同于國際標準 ISO/IEC 646。由于萬維網(wǎng)使得 ASCII 廣為通用,直到 2007 年 12 月,逐漸被 Unicode 取代。

上面的引文中已經(jīng)說了,現(xiàn)在我們用的編碼標準已經(jīng)變成 Unicode 了,那么什么是 Unicode 呢?還是抄一段來自維基百科的說明

Unicode(中文:萬國碼、國際碼、統(tǒng)一碼、單一碼)是計算機科學領域里的一項業(yè)界標準。它對世界上大部分的文字系統(tǒng)進行了整理、編碼,使得電腦可以用更為簡單的方式來呈現(xiàn)和處理文字。

Unicode 伴隨著通用字符集的標準而發(fā)展,同時也以書本的形式對外發(fā)表。Unicode 至今仍在不斷增修,每個新版本都加入更多新的字符。目前最新的版本為 7.0.0,已收入超過十萬個字符(第十萬個字符在 2005 年獲采納)。Unicode 涵蓋的數(shù)據(jù)除了視覺上的字形、編碼方法、標準的字符編碼外,還包含了字符特性,如大小寫字母。

聽這名字:萬國碼,那就一定包含了中文嘍。的確是。但是,光有一個 Unicode 還不行,因為....(此處省略若干字,看官可以到上面給出的維基百科鏈接中看),還要有其它的一些編碼實現(xiàn)方式,Unicode 的實現(xiàn)方式稱為 Unicode 轉(zhuǎn)換格式(Unicode Transformation Format,簡稱為 UTF),于是乎有了一個我們在很多時候都會看到的 utf-8。

什么是 utf-8,還是看維基百科上怎么說的吧

UTF-8(8-bit Unicode Transformation Format)是一種針對 Unicode 的可變長度字符編碼,也是一種前綴碼。它可以用來表示 Unicode 標準中的任何字符,且其編碼中的第一個字節(jié)仍與 ASCII 兼容,這使得原來處理 ASCII 字符的軟件無須或只須做少部份修改,即可繼續(xù)使用。因此,它逐漸成為電子郵件、網(wǎng)頁及其他存儲或發(fā)送文字的應用中,優(yōu)先采用的編碼。

不再多引用了,如果要看更多,請到原文。

看官現(xiàn)在是不是就理解了,前面寫程序的時候,曾經(jīng)出現(xiàn)過:coding:utf-8 的字樣。就是在告訴 python 我們要用什么字符編碼呢。

encode 和 decode

歷史部分說完了,接下怎么講?比較麻煩了。因為不管怎么講,都不是三言兩語說清楚的。姑且從 encode()和 decode()兩個內(nèi)置函數(shù)起吧。

codecs.encode(obj[, encoding[, errors]]):Encodes obj using the codec registered for encoding. codecs.decode(obj[, encoding[, errors]]):Decodes obj using the codec registered for encoding.

Python2 默認的編碼是 ascii,通過 encode 可以將對象的編碼轉(zhuǎn)換為指定編碼格式(稱作“編碼”),而 decode 是這個過程的逆過程(稱作“解碼”)。

做一個實驗,才能理解:

>>> a = "中"
>>> type(a)
<type 'str'>
>>> a
'\xe4\xb8\xad'
>>> len(a)
3

>>> b = a.decode()
>>> b
u'\u4e2d'
>>> type(b)
<type 'unicode'>
>>> len(b)
1

這個實驗不做之前,或許看官還不是很迷茫(因為不知道,知道的越多越迷茫),實驗做完了,自己也迷茫了。別急躁,對編碼問題的理解,要慢慢來,如果一時理解不了,也肯定理解不了,就先注意按照要求做,做著做著就豁然開朗了。

上面試驗中,變量 a 引用了一個字符串,所謂字符串(str),嚴格地將是字節(jié)串,它是經(jīng)過編碼后的字節(jié)組成的序列。也就是你在上面的實驗中,看到的是“中”這個字在計算機中編碼之后的字節(jié)表示。(關于字節(jié),看官可以 google 一下)。用 len(a)來度量它的長度,它是由三個字節(jié)組成的。

然后通過 decode 函數(shù),將字節(jié)串轉(zhuǎn)變?yōu)?strong>字符串,并且這個字符串是按照 unicode 編碼的。在 unicode 編碼中,一個漢字對應一個字符,這時候度量它的長度就是 1.

反過來,一個 unicode 編碼的字符串,也可以轉(zhuǎn)換為字節(jié)串。

>>> c = b.encode('utf-8')
>>> c
'\xe4\xb8\xad'
>>> type(c)
<type 'str'>
>>> c == a
True

關于編碼問題,先到這里,點到為止吧。因為再扯,還會扯出問題來??垂倏隙ǜ械讲粷M意,因為還沒有知其所以然。沒關系,請盡情 google,即可解決。

Python 中如何避免中文是亂碼

這個問題是一個具有很強操作性的問題。我這里有一個經(jīng)驗總結(jié),分享一下,供參考:

首先,提倡使用 utf-8 編碼方案,因為它跨平臺不錯。

經(jīng)驗一:在開頭聲明:

# -*- coding: utf-8 -*-

有朋友問我-*-有什么作用,那個就是為了好看,愛美之心人皆有,更何況程序員?當然,也可以寫成:

# coding:utf-8

經(jīng)驗二:遇到字符(節(jié))串,立刻轉(zhuǎn)化為 unicode,不要用 str(),直接使用 unicode()

unicode_str = unicode('中文', encoding='utf-8')
print unicode_str.encode('utf-8')

經(jīng)驗三:如果對文件操作,打開文件的時候,最好用 codecs.open,替代 open(這個后面會講到,先放在這里)

import codecs
codecs.open('filename', encoding='utf8')

我還收集了網(wǎng)上的一片文章,也挺好的,推薦給看官:Python2.x的中文顯示方法

最后告訴給我,如果用 Python3,坑爹的編碼問題就不煩惱了。


總目錄   |   上節(jié):字符串(4)   |   下節(jié):列表(1)

如果你認為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。