python2與python3字符串的區別

python2

在 py2 中,有兩種類型字符串:str 和 unicode。但嚴格的來講,str 並非徹底意義上的字符串,把它稱做 字節碼串 更合適。unicode 則做爲真正意義上的 字符串,但定義時須要使用 u"" 去聲明。有人可能會有疑問,字符串 在內存單元中也是以字節進行存儲的,怎麼能將他倆區別對待呢?java

字符串 是一種 數據結構。數據結構 = 原數據 + 數據描述(爲了更容易理解),而字符串的 「數據描述」 即爲什麼種字符集。python

字節碼 則是沒有數據描述的。數組

py2 中,當咱們讀取 str 類型時,它只表明了一串數據,至於這串數據是何種編碼,系統是不知道的,須要咱們手動指定。而 unicode 類型,則存儲的相應的字符集,系統在讀取時便能根據此數據描述對數據進行識別和處理。數據結構

'\xc4\xe3\xba\xc3' 對計算機來講,就是 1 個字節的字節碼串在一塊兒。編碼

u'\u4f60\u597d' 對計算機來講,則是每 2 個字節做爲一個單位,去映射 unicode 字符集作運算。spa

當咱們定義 str字符串 時,python 會根據咱們當前的運行上下文:源文件 / cli 去設定 str 的字符集。好比咱們在腳本開始處註釋的 coding: utf-8 會將腳本中的 str 編碼爲 utf-8 的字節碼串兒。操作系統

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

import sys

"""
做用是註明腳本上下文的字符集
好比當咱們定義一個 str 類型的數據時
它是 utf-8 字符集的字節碼
"""
str = "你好" #以 utf-8 字節碼去編碼

"""
但不要和 python 的默認字符集搞混
默認字符集會在一些隱式加解碼時被運用
"""
# ascii
print sys.getdefaultencoding()

# 設置 python 的默認字符集
reload(sys)
sys.setdefaultencoding("utf-8")

"""
下面的代碼存在一次隱式的 decode
在隱式或沒有指定 decode 的字符集時
會使用 默認字符集(這裏已將默認字符集設爲 utf-8)進行解碼
若是咱們沒有修改默認字符集而使用 ascii 的話
就會報錯了 ascii 是沒辦法編碼中文的
"""
"你好".encode("gb2312")


"""
py2中 write 寫的爲字節碼串
當前源碼編碼字符集爲 utf-8
因此寫入時 "你好" 是以 utf-8 編碼的
"""
with open("encode.txt", "w") as f:
    f.write("你好")

"""
讀取的 str 也爲 utf-8 的字節碼串
"""
with open("encode.txt", "r") as f:
    str = f.read()

例如個人 cmd 是 gb2312 字符集,因此 "你好" 的字節碼串以下(gb2312 一箇中文字符用 2 個字節標識)。下面咱們將其轉換爲 utf-8 字符集的字節碼:code

encode/decode 下面詳細講解。內存

python3

py3 則對字符串和字節碼進行了更爲規範的定義:str 和 bytes。str 終於成爲了真正意義上的字符串,bytes 也形象的表徵了字節碼串兒(java 中的字節數組)utf-8

py3 默認支持 unicode 字符集,故不須要雞肋的使用 py2 中的 u"xxx" 去定義一個 unicode 字符串。

簡單來講,py2 想定義一個平常的字符串,須要手動 u"xxx","xxx" 定義的是當前編碼設定下的字節碼串兒,典型的本末倒置。在 py3 中,再也不有 u"xxx" 的語法,"xxx" 定義的就是字符串(unicode編碼),b"xxx" 定義的纔是字節碼串兒。

"""
py3 中消除了py2中字節碼和字符串混亂的定義
str 就是字符串 bytes 纔是字節碼
在 py2 中 str 是字節碼 unicode 纔是字符串
"""
        py3        py2
字節碼  bytes      str
字符串  str       unicode

因此在 py3 中,你的 coding: utf-8 的註釋沒有任何用處了。在 py2 中它會隱式的根據指定編碼對 str 作字節碼的定義,但在 py3 中已經沒有這種隱式的轉換和定義了。

import sys

# 默認字符集由 ascii 改成 utf-8
print("默認字符集爲:%s" % (sys.getdefaultencoding()))

str = "你好"

# 2 個字符長度 py2 中就不會把 str 看爲數據結構 而是字節碼 因此結果會是 6
print(len(str))


# 不指定字符集將使用默認的 utf-8 進行編碼
str.encode() # b'\xe4\xbd\xa0\xe5\xa5\xbd'
str.encode("utf-8") # b'\xe4\xbd\xa0\xe5\xa5\xbd'
str.encode("gb2312") # b'\xc4\xe3\xba\xc3'

hello_utf8   = b'\xe4\xbd\xa0\xe5\xa5\xbd'
hello_gb2312 = b'\xc4\xe3\xba\xc3'

# utf-8 轉 gb2312 / gb2312 轉 utf-8
# 都是以 unicode 做爲基準進行操做
hello_utf8.decode("utf-8").encode("gb2312")
hello_gb2312.decode("gb2312").encode("utf-8")

 

encode/decode

py 以 unicode 做爲字符運算的基準。

bytes  --  decode --  unicode --  encode --  bytes

unicode 編碼 至其餘字符集(utf-8/gb2312),其餘字符集 解碼 至 unicode

在 py2 中常常遇到的錯誤:

"你好".encode("utf-8")

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 0: ordinal not in range(128)

錯誤的緣由是什麼呢?py2 以 ascii 做爲默認字符集,"你好" 直接 encode 至其餘編碼時會進行一次隱式的解碼。由於 py 以 unicode 做爲運算基準,因此會先 decode 至 unicode,你沒使用 py 就隱式執行,使用默認字符集 ascii 作解碼,天然沒法解碼中文字符的字節碼。

而在 py3 中是不容許這種操做的。從 bytes 到另外一 bytes 你必須先 decode 後才能 encode。

open/read/write

py2 中,open 方法並不會設定以何種字符集去打開文件,由於 py 要的不是字符串,而是字節碼串兒。是的,read 和 write 操做的都是字節碼串兒,字節碼串兒自己並不具有表徵字符的能力,咱們須要手動的指定正確的字符集進行解碼。

# write.py
# -*- coding: utf-8 -*-
with open("hello.txt", "w") as f:
    f.write("你好") # 這裏實際上是字節碼

# read.py
# -*- coding: gbk -*-
# 結果爲 6 仍然是以 utf-8 的字節碼去計算的
with open("hello.txt", "r") as f:
    str = f.read()
    print len(str) # 讀取的也是字節碼

數據會以相應的字節碼去保存

py3 中,由於存儲時傳入的是 unicode 字符串(而再也不是 py2 的字節碼),但數據存儲確定是以字節碼的形式(物理存儲)。因此 py3 確定會將字符串轉爲字節碼(這個過程是py3本身完成的,咱們此時但是指定以何種字符集去編碼),這時就涉及採用何種編碼去進行隱式的 encode 存儲和 decode 讀取了。

中文系統,使用 python xxx.py 時上下文的編碼爲操做系統的編碼:gbk。這裏挺饒的,雖然我是在 gbk2312 的 cmd 裏運行的,但並不是是交互模式,因此輸入時並不是使用的 cmd 的字符集,而是 python 解釋器啓動時讀取的本地系統的字符集:gbk。

#-*-coding: utf-8 -*-
"""
py3 的默認字符集爲 utf-8 因此平常開發中咱們是能夠省略 encoding 的
"""
with open("hello.txt", "w", encoding = "utf-8") as f:
    f.write("你好")

with open("hello.txt", "r", encoding = "utf-8") as f:
    print(f.read())
    
# -*-coding: utf-8 -*- 就算我聲明瞭此行註釋
# py3 依然不會使用其做爲 open 打開文件時默認的 encoding
# 也不會使用 py3 自身默認的 sys.getdefaultencoding
# 這裏 open 的 encoding 默認會與你的本地系統相關
# 因此下面我須要使用 gbk 返回字節碼,從新使用 utf-8 正確解碼
with open("hello.txt", "r") as f:
    print(f.read().encode("gbk").decode('utf-8')) #

# 以 b(二進制) 方式讀取時爲讀取的字節碼串兒
# 因此數據並不會受編碼影響
# 咱們使用時須要指定正確的解碼字符集
with open("hello.txt", "rb") as f:
    bytes = f.read()
    print(bytes)
    print(len(bytes)) # 6 utf-8 的字節碼 一箇中文3個字節
    print(bytes.decode("utf-8")) #正常解碼 得到 "你好" unicode 字符串
    # bytes.decode("gb2312") #錯誤解碼

因此,coding: utf-8 貌似已經沒什麼用處了

相關文章
相關標籤/搜索