在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 字符編碼
基礎(chǔ)
itertools
HTTP 服務(wù)
hashlib
閉包
文件和目錄
單元測(cè)試
使用 @property
標(biāo)準(zhǔn)模塊
陌生的 metaclass
Base64
進(jìn)程、線程和協(xié)程
讀寫二進(jìn)制文件
匿名函數(shù)
輸入和輸出
Click
元組
字符編碼
partial 函數(shù)
參考資料
collections
協(xié)程
類和實(shí)例
Python 之旅
定制類和魔法方法
常用數(shù)據(jù)類型
繼承和多態(tài)
ThreadLocal
HTTP 協(xié)議簡介
Requests 庫的使用
讀寫文本文件
列表
os 模塊
迭代器 (Iterator)
正則表達(dá)式
集合
上下文管理器
異常處理
你不知道的 super
定義函數(shù)
datetime
資源推薦
字典
slots 魔法
hmac
第三方模塊
進(jìn)程
類方法和靜態(tài)方法
函數(shù)參數(shù)
高階函數(shù)
函數(shù)
re 模塊
高級(jí)特性
線程
argparse
生成器
結(jié)束語
字符串
map/reduce/filter
函數(shù)式編程
Celery
裝飾器

字符編碼

字符編碼是計(jì)算機(jī)編程中不可回避的問題,不管你用 Python2 還是 Python3,亦或是 C++, Java 等,我都覺得非常有必要厘清計(jì)算機(jī)中的字符編碼概念。本文主要分以下幾個(gè)部分介紹:

  • 基本概念
  • 常見字符編碼簡介
  • Python 的默認(rèn)編碼
  • Python2 中的字符類型
  • UnicodeEncodeError & UnicodeDecodeError 根源

基本概念

  • 字符(Character)

在電腦和電信領(lǐng)域中,字符是一個(gè)信息單位,它是各種文字和符號(hào)的總稱,包括各國家文字、標(biāo)點(diǎn)符號(hào)、圖形符號(hào)、數(shù)字等。比如,一個(gè)漢字,一個(gè)英文字母,一個(gè)標(biāo)點(diǎn)符號(hào)等都是一個(gè)字符。

  • 字符集(Character set)

字符集是字符的集合。字符集的種類較多,每個(gè)字符集包含的字符個(gè)數(shù)也不同。比如,常見的字符集有 ASCII 字符集、GB2312 字符集、Unicode 字符集等,其中,ASCII 字符集共有 128 個(gè)字符,包含可顯示字符(比如英文大小寫字符、阿拉伯?dāng)?shù)字)和控制字符(比如空格鍵、回車鍵);GB2312 字符集是中國國家標(biāo)準(zhǔn)的簡體中文字符集,包含簡化漢字、一般符號(hào)、數(shù)字等;Unicode 字符集則包含了世界各國語言中使用到的所有字符,

  • 字符編碼(Character encoding)

字符編碼,是指對(duì)于字符集中的字符,將其編碼為特定的二進(jìn)制數(shù),以便計(jì)算機(jī)處理。常見的字符編碼有 ASCII 編碼,UTF-8 編碼,GBK 編碼等。一般而言,字符集字符編碼往往被認(rèn)為是同義的概念,比如,對(duì)于字符集 ASCII,它除了有「字符的集合」這層含義外,同時(shí)也包含了「編碼」的含義,也就是說,ASCII 既表示了字符集也表示了對(duì)應(yīng)的字符編碼

下面我們用一個(gè)表格做下總結(jié):

概念 概念描述 舉例
字符 一個(gè)信息單位,各種文字和符號(hào)的總稱 ‘中’, ‘a(chǎn)', ‘1', '$', ‘¥’, ...?
字符集 字符的集合 ASCII 字符集, GB2312 字符集, Unicode 字符集
字符編碼 將字符集中的字符,編碼為特定的二進(jìn)制數(shù) ASCII 編碼,GB2312 編碼,Unicode 編碼
字節(jié) 計(jì)算機(jī)中存儲(chǔ)數(shù)據(jù)的單元,一個(gè) 8 位(bit)的二進(jìn)制數(shù) 0x01, 0x45, ...

常見字符編碼簡介

常見的字符編碼有 ASCII 編碼,GBK 編碼,Unicode 編碼和 UTF-8 編碼等等。這里,我們主要介紹 ASCII、Unicode 和 UTF-8。

ASCII

計(jì)算機(jī)是在美國誕生的,人家用的是英語,而在英語的世界里,不過就是英文字母,數(shù)字和一些普通符號(hào)的組合而已。

在 20 世紀(jì) 60 年代,美國制定了一套字符編碼方案,規(guī)定了英文字母,數(shù)字和一些普通符號(hào)跟二進(jìn)制的轉(zhuǎn)換關(guān)系,被稱為 ASCII (American Standard Code for Information Interchange,美國信息互換標(biāo)準(zhǔn)編碼) 碼。

比如,大寫英文字母 A 的二進(jìn)制表示是 01000001(十進(jìn)制 65),小寫英文字母 a 的二進(jìn)制表示是 01100001 (十進(jìn)制 97),空格 SPACE 的二進(jìn)制表示是 00100000(十進(jìn)制 32)。

Unicode

ASCII 碼只規(guī)定了 128 個(gè)字符的編碼,這在美國是夠用的??墒?,計(jì)算機(jī)后來傳到了歐洲,亞洲,乃至世界各地,而世界各國的語言幾乎是完全不一樣的,用 ASCII 碼來表示其他語言是遠(yuǎn)遠(yuǎn)不夠的,所以,不同的國家和地區(qū)又制定了自己的編碼方案,比如中國大陸的 GB2312 編碼 和 GBK 編碼等,日本的 Shift_JIS 編碼等等。

雖然各個(gè)國家和地區(qū)可以制定自己的編碼方案,但不同國家和地區(qū)的計(jì)算機(jī)在數(shù)據(jù)傳輸?shù)倪^程中就會(huì)出現(xiàn)各種各樣的亂碼(mojibake),這無疑是個(gè)災(zāi)難。

怎么辦?想法也很簡單,就是將全世界所有的語言統(tǒng)一成一套編碼方案,這套編碼方案就叫 Unicode,它為每種語言的每個(gè)字符設(shè)定了獨(dú)一無二的二進(jìn)制編碼,這樣就可以跨語言,跨平臺(tái)進(jìn)行文本處理了,是不是很棒!

Unicode 1.0 版誕生于 1991 年 10 月,至今它仍在不斷增修,每個(gè)新版本都會(huì)加入更多新的字符,目前最新的版本為 2016 年 6 月 21 日公布的 9.0.0。

Unicode 標(biāo)準(zhǔn)使用十六進(jìn)制數(shù)字,而且在數(shù)字前面加上前綴 U+,比如,大寫字母「A」的 unicode 編碼為 U+0041,漢字「嚴(yán)」的 unicode 編碼為 U+4E25。更多的符號(hào)對(duì)應(yīng)表,可以查詢 unicode.org,或者專門的漢字對(duì)應(yīng)表。

UTF-8

Unicode 看起來已經(jīng)很完美了,實(shí)現(xiàn)了大一統(tǒng)。但是,Unicode 卻存在一個(gè)很大的問題:資源浪費(fèi)。

為什么這么說呢?原來,Unicode 為了能表示世界各國所有文字,一開始用兩個(gè)字節(jié),后來發(fā)現(xiàn)兩個(gè)字節(jié)不夠用,又用了四個(gè)字節(jié)。比如,漢字「嚴(yán)」的 unicode 編碼是十六進(jìn)制數(shù) 4E25,轉(zhuǎn)換成二進(jìn)制有十五位,即 100111000100101,因此至少需要兩個(gè)字節(jié)才能表示這個(gè)漢字,但是對(duì)于其他的字符,就可能需要三個(gè)或四個(gè)字節(jié),甚至更多。

這時(shí),問題就來了,如果以前的 ASCII 字符集也用這種方式來表示,那豈不是很浪費(fèi)存儲(chǔ)空間。比如,大寫字母「A」的二進(jìn)制編碼為 01000001,它只需要一個(gè)字節(jié)就夠了,如果 unicode 統(tǒng)一使用三個(gè)字節(jié)或四個(gè)字節(jié)來表示字符,那「A」的二進(jìn)制編碼的前面幾個(gè)字節(jié)就都是 0,這是很浪費(fèi)存儲(chǔ)空間的。

為了解決這個(gè)問題,在 Unicode 的基礎(chǔ)上,人們實(shí)現(xiàn)了 UTF-16, UTF-32 和 UTF-8。下面只說一下 UTF-8。

UTF-8 (8-bit Unicode Transformation Format) 是一種針對(duì) Unicode 的可變長度字符編碼,它使用一到四個(gè)字節(jié)來表示字符,例如,ASCII 字符繼續(xù)使用一個(gè)字節(jié)編碼,阿拉伯文、希臘文等使用兩個(gè)字節(jié)編碼,常用漢字使用三個(gè)字節(jié)編碼,等等。

因此,我們說,UTF-8 是 Unicode 的實(shí)現(xiàn)方式之一,其他實(shí)現(xiàn)方式還包括 UTF-16(字符用兩個(gè)或四個(gè)字節(jié)表示)和 UTF-32(字符用四個(gè)字節(jié)表示)。

Python 的默認(rèn)編碼

Python2 的默認(rèn)編碼是 ascii,Python3 的默認(rèn)編碼是 utf-8,可以通過下面的方式獲?。?/p>

  • Python2
Python 2.7.11 (default, Feb 24 2016, 10:48:05)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
  • Python3
Python 3.5.2 (default, Jun 29 2016, 13:43:58)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'

Python2 中的字符類型

Python2 中有兩種和字符串相關(guān)的類型:str 和 unicode,它們的父類是 basestring。其中,str 類型的字符串有多種編碼方式,默認(rèn)是 ascii,還有 gbk,utf-8 等,unicode 類型的字符串使用 u'...' 的形式來表示,下面的圖展示了 str 和 unicode 之間的關(guān)系:

sm.ms

兩種字符串的相互轉(zhuǎn)換概括如下:

  • 把 UTF-8 編碼表示的字符串 'xxx' 轉(zhuǎn)換為 Unicode 字符串 u'xxx' 用 decode('utf-8') 方法:
>>> '中文'.decode('utf-8')
u'\u4e2d\u6587'
  • 把 u'xxx' 轉(zhuǎn)換為 UTF-8 編碼的 'xxx' 用 encode('utf-8') 方法:
>>> u'中文'.encode('utf-8')
'\xe4\xb8\xad\xe6\x96\x87'

UnicodeEncodeError & UnicodeDecodeError 根源

用 Python2 編寫程序的時(shí)候經(jīng)常會(huì)遇到 UnicodeEncodeError 和 UnicodeDecodeError,它們出現(xiàn)的根源就是如果代碼里面混合使用了 str 類型和 unicode 類型的字符串,Python 會(huì)默認(rèn)使用 ascii 編碼嘗試對(duì) unicode 類型的字符串編碼 (encode),或?qū)?str 類型的字符串解碼 (decode),這時(shí)就很可能出現(xiàn)上述錯(cuò)誤

下面有兩個(gè)常見的場(chǎng)景,我們最好牢牢記住:

  • 在進(jìn)行同時(shí)包含 str 類型和 unicode 類型的字符串操作時(shí),Python2 一律都把 str 解碼(decode)成 unicode 再運(yùn)算,這時(shí)就很容易出現(xiàn) UnicodeDecodeError。

讓我們看看例子:

>>> s = '你好'    # str 類型, utf-8 編碼
>>> u = u'世界'   # unicode 類型
>>> s + u        # 會(huì)進(jìn)行隱式轉(zhuǎn)換,即 s.decode('ascii') + u
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

為了避免出錯(cuò),我們就需要顯示指定使用 'utf-8' 進(jìn)行解碼,如下:

>>> s = '你好'                 # str 類型,utf-8 編碼
>>> u = u'世界'
>>>
>>> s.decode('utf-8') + u     # 顯示指定 'utf-8' 進(jìn)行轉(zhuǎn)換
u'\u4f60\u597d\u4e16\u754c'   # 注意這不是錯(cuò)誤,這是 unicode 字符串
  • 如果函數(shù)或類等對(duì)象接收的是 str 類型的字符串,但你傳的是 unicode,Python2 會(huì)默認(rèn)使用 ascii 將其編碼成 str 類型再運(yùn)算,這時(shí)就很容易出現(xiàn) UnicodeEncodeError。

讓我們看看例子:

>>> u_str = u'你好'
>>> str(u_str)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

在上面的代碼中,u_str 是一個(gè) unicode 類型的字符串,由于 str() 的參數(shù)只能是 str 類型,此時(shí) Python 會(huì)試圖使用 ascii 將其編碼成 ascii,也就是:

u_str.encode('ascii')   // u_str 是 unicode 字符串

上面將 unicode 類型的中文使用 ascii 編碼轉(zhuǎn),肯定會(huì)出錯(cuò)。

再看一個(gè)使用 raw_input 的例子,注意 raw_input 只接收 str 類型的字符串:

>>> name = raw_input('input your name: ')
input your name: ethan
>>> name
'ethan'

>>> name = raw_input('輸入你的姓名:')
輸入你的姓名: 小明
>>> name
'\xe5\xb0\x8f\xe6\x98\x8e'
>>> type(name)
<type 'str'>

>>> name = raw_input(u'輸入你的姓名: ')   # 會(huì)試圖使用 u'輸入你的姓名'.encode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, 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')) #可以,但此時(shí) name 不是 unicode 類型
輸入你的姓名: 小明
>>> name
'\xe5\xb0\x8f\xe6\x98\x8e'
>>> type(name)
<type 'str'>

>>> name = raw_input(u'輸入你的姓名: '.encode('utf-8')).decode('utf-8') # 推薦
輸入你的姓名:  小明
>>> name
u'\u5c0f\u660e'
>>> type(name)
<type 'unicode'>

再看一個(gè)重定向的例子:

hello = u'你好'
print hello

將上面的代碼保存到文件 hello.py,在終端執(zhí)行 python hello.py 可以正常打印,但是如果將其重定向到文件 python hello.py > result 會(huì)發(fā)現(xiàn) UnicodeEncodeError。

這是因?yàn)椋狠敵龅娇刂婆_(tái)時(shí),print 使用的是控制臺(tái)的默認(rèn)編碼,而重定向到文件時(shí),print 就不知道使用什么編碼了,于是就使用了默認(rèn)編碼 ascii 導(dǎo)致出現(xiàn)編碼錯(cuò)誤。

應(yīng)該改成如下:

hello = u'你好'
print hello.encode('utf-8')

這樣執(zhí)行 python hello.py > result 就沒有問題。

小結(jié)

  • UTF-8 是一種針對(duì) Unicode 的可變長度字符編碼,它是 Unicode 的實(shí)現(xiàn)方式之一。
  • Unicode 字符集有多種編碼標(biāo)準(zhǔn),比如 UTF-8, UTF-7, UTF-16。
  • 在進(jìn)行同時(shí)包含 str 類型和 unicode 類型的字符串操作時(shí),Python2 一律都把 str 解碼(decode)成 unicode 再運(yùn)算。
  • 如果函數(shù)或類等對(duì)象接收的是 str 類型的字符串,但你傳的是 unicode,Python2 會(huì)默認(rèn)使用 ascii 將其編碼成 str 類型再運(yùn)算。

參考資料

上一篇:函數(shù)下一篇:列表