1. 程式人生 > >Python 編碼(一)— Python3

Python 編碼(一)— Python3

mar 令行 sock 早期 ref 亂碼 transform enc 方法

Unicode

什麽是 Unicode

標準 unicode

標準 Unicode 為每個字符提供了一個獨特的數字,並且跨平臺、設備、應用或者編程語言都是通用的。 -- 來自 http://unicode.org/standard/WhatIsUnicode.html

Unicode

什麽是 Unicode

標準 unicode

標準 Unicode 為每個字符提供了一個獨特的數字,並且跨平臺、設備、應用或者編程語言都是通用的。 -- 來自 http://unicode.org/standard/WhatIsUnicode.html

Unicode 之前的編碼

比如 ASCII、GBK等等。

這些早期的字符編碼是受限制的並且不能包含包含全世界語言的編碼。

早期的字符編碼互相之間也會沖突。兩種編碼可能使用同樣的數字來表示不同的字符或者使用不同的數字來表示同樣的字符。任意給定的計算機(尤其是服務器)會需要支持多種不同的編碼。然而當數據在不同計算機或不同編碼之間傳遞的時候,數據會有沖突的風險。 -- 來自 https://zh.wikipedia.org/wiki/Unicode#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F

UTF

UTF(Unicode Transformation Format) 的意思是 Unicode 轉換格式。

例如,如果一個僅包含基本7位ASCII字符的Unicode文件,如果每個字符都使用2字節的原Unicode編碼傳輸,其第一字節的8位始終為0。這就造成了比較大的浪費。對於這種情況,可以使用UTF-8編碼,這是一種變長編碼,它將基本7位ASCII字符仍用7位編碼表示,占用一個字節(首位補0)。而遇到與其他Unicode字符混合的情況,將按一定算法轉換,每個字符使用1-3個字節編碼,並利用首位為0或1進行識別。這樣對以7位ASCII字符為主的西文文檔就大幅節省了編碼長度(具體方案參見UTF-8)。類似的,對未來會出現的需要4個字節的輔助平面字符和其他UCS-4擴充字符,2字節編碼的UTF-16也需要通過一定的算法進行轉換。 -- 來自 https://zh.wikipedia.org/wiki/Unicode#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F

Python 編碼

Unicode 是一連串的數字。

Python 編碼指將 Unicode 轉換為 bytes。 -- 來自 https://docs.python.org/3/howto/unicode.html#encodings

對於 ASCII 編碼:

  1. 如果編碼點小於 128,每個比特與編碼點的值相同
  2. 如果編碼點大於等於 128,那麽這些 Unicode 字符不能使用這種編碼表示。(Python 會拋出 UnicodeEncodeError)
    -- 來自 https://zh.wikipedia.org/wiki/Unicode#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F

UTF-8 是最常用的編碼,有如下方便的性質:

  1. 可以處理所有 Unicode 編碼點。
  2. ASCII 文本也是有效的 UTF-8 文本。
  3. UTF-8 很緊湊;常用的字符可以使用一個或者兩個 bytes 表示。
    -- 來自 https://zh.wikipedia.org/wiki/Unicode#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F

Python3 對 Unicode 的支持

從 Python 3.0 開始,使用 Unicode 儲存字符串。

Python 源碼的默認編碼是 UTF-8,也可以通過 # -*- coding: <encoding name> -*- 來指定特殊的編碼。

讀寫 Unicode 數據

Unicode 數據在寫入磁盤或者發送到一個 socket 前通常會被轉化為一種編碼。你可以自己完成所有的工作:打開一個文件,從文件中讀取 8-bit bytes 然後使用 bytes.decode(encoding) 轉換 bytes。但是不推薦手動處理。

一個原因是一個 Unicode 字符可以被多個 bytes 表示。如果你讀取任意大小的塊(比如 1024 或者 4096 bytes),你需要寫錯誤處理代碼來捕捉塊的末尾部分 Unicode 字符不完整的情況。一個解決辦法是讀取整個文件到內存中,但是這會使你不能處理大文件。

解決辦法是使用低級別的解碼接口來捕捉部分編碼序列的情況。這個工作已經被自帶的 open() 函數實現了,open(filename, encoding=encoding) 返回一個可以擁有如 read()write() 等方法的 file-like 對象。
-- 以上引用來自 https://docs.python.org/3/howto/unicode.html#reading-and-writing-unicode-data

Unicode 在編程中的技巧

軟件內部應該只使用 Unicode 字符串,盡快解碼輸入數據(bytes)並只在最後給輸出編碼。

當使用來自瀏覽器或者其他不信任來源的數據時,一個常用的技巧是在使用字符串作為命令行或者儲存字符串到數據庫前檢查字符串中的非法字符。如果你打算這樣做,要註意檢查解碼後的字符串,而不是編碼的 bytes 數據;因為一些編碼可能有一些有趣的屬性,比如有多個意思或者不是完全適配 ASCII。 -- 來自 https://docs.python.org/3/howto/unicode.html#tips-for-writing-unicode-aware-programs

未知編碼的文件

如果你知道文件的編碼是適配 ASCII 的並且只想測試或修改 ASCII 的部分,你可以用 surrogateescape 錯誤處理器來打開文件。

with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
    data = f.read()

# make changes to the string 'data'

with open(fname + '.new', 'w',
          encoding="ascii", errors="surrogateescape") as f:
    f.write(data)

surrogateescape 錯誤處理器將所有非 ASCII bytes 解碼為 Unicode 編碼點。這些秘密編碼點會變回同樣的 bytes 當使用 surrogateescape 編碼數據並寫出的時候。
-- 來自 https://docs.python.org/3/howto/unicode.html#files-in-an-unknown-encoding

假設文件只有一種編碼,那麽可以嘗試使用 所有標準編碼 進行解碼,從解碼沒有報錯的結果中挑選出合適的,即沒有亂碼的結果。
Unicode 之前的編碼

比如 ASCII、GBK等等。

這些早期的字符編碼是受限制的並且不能包含包含全世界語言的編碼。

早期的字符編碼互相之間也會沖突。兩種編碼可能使用同樣的數字來表示不同的字符或者使用不同的數字來表示同樣的字符。任意給定的計算機(尤其是服務器)會需要支持多種不同的編碼。然而當數據在不同計算機或不同編碼之間傳遞的時候,數據會有沖突的風險。 -- 來自 https://zh.wikipedia.org/wiki/Unicode#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F

UTF

UTF(Unicode Transformation Format) 的意思是 Unicode 轉換格式。

例如,如果一個僅包含基本7位ASCII字符的Unicode文件,如果每個字符都使用2字節的原Unicode編碼傳輸,其第一字節的8位始終為0。這就造成了比較大的浪費。對於這種情況,可以使用UTF-8編碼,這是一種變長編碼,它將基本7位ASCII字符仍用7位編碼表示,占用一個字節(首位補0)。而遇到與其他Unicode字符混合的情況,將按一定算法轉換,每個字符使用1-3個字節編碼,並利用首位為0或1進行識別。這樣對以7位ASCII字符為主的西文文檔就大幅節省了編碼長度(具體方案參見UTF-8)。類似的,對未來會出現的需要4個字節的輔助平面字符和其他UCS-4擴充字符,2字節編碼的UTF-16也需要通過一定的算法進行轉換。 -- 來自 https://zh.wikipedia.org/wiki/Unicode#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F

Python 編碼

Unicode 是一連串的數字。

Python 編碼指將 Unicode 轉換為 bytes。 -- 來自 https://docs.python.org/3/howto/unicode.html#encodings

對於 ASCII 編碼:

  1. 如果編碼點小於 128,每個比特與編碼點的值相同
  2. 如果編碼點大於等於 128,那麽這些 Unicode 字符不能使用這種編碼表示。(Python 會拋出 UnicodeEncodeError)
    -- 來自 https://zh.wikipedia.org/wiki/Unicode#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F

UTF-8 是最常用的編碼,有如下方便的性質:

  1. 可以處理所有 Unicode 編碼點。
  2. ASCII 文本也是有效的 UTF-8 文本。
    UTF-8 很緊湊;常用的字符可以使用一個或者兩個 bytes 表示。
    -- 來自 https://zh.wikipedia.org/wiki/Unicode#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F

Python3 對 Unicode 的支持

從 Python 3.0 開始,使用 Unicode 儲存字符串。

Python 源碼的默認編碼是 UTF-8,也可以通過 # -*- coding: <encoding name> -*- 來指定特殊的編碼。

讀寫 Unicode 數據

Unicode 數據在寫入磁盤或者發送到一個 socket 前通常會被轉化為一種編碼。你可以自己完成所有的工作:打開一個文件,從文件中讀取 8-bit bytes 然後使用 bytes.decode(encoding) 轉換 bytes。但是不推薦手動處理。

一個原因是一個 Unicode 字符可以被多個 bytes 表示。如果你讀取任意大小的塊(比如 1024 或者 4096 bytes),你需要寫錯誤處理代碼來捕捉塊的末尾 Unicode 字符不完整的情況。一個解決辦法是讀取整個文件到內存中,但是這會讓你不能處理大文件。

解決辦法是使用低級別的解碼接口來捕捉部分編碼序列的情況。這個工作已經被自帶的 open() 函數實現了,open(filename, encoding=encoding) 返回一個可以擁有如 read()write() 等方法的 file-like 對象。
-- 以上引用來自 https://docs.python.org/3/howto/unicode.html#reading-and-writing-unicode-data

編寫註意 Unicode 的程序的技巧

軟件內部應該只使用 Unicode 字符串,盡快解碼輸入數據(bytes)並只在最後給輸出編碼。

當使用來自瀏覽器或者其他不信任來源的數據時,一個常用的技巧是在使用字符串作為命令行或者儲存字符串到數據庫前檢查字符串中的非法字符。如果你打算這樣做,要註意檢查解碼後的字符串,而不是編碼的 bytes 數據;因為一些編碼可能有一些有趣的屬性,比如有多個意思或者不是完全適配 ASCII。 -- 來自 https://docs.python.org/3/howto/unicode.html#tips-for-writing-unicode-aware-programs

未知編碼的文件

如果你知道文件的編碼是適配 ASCII 的並且只想測試或修改 ASCII 的部分,你可以用 surrogateescape 錯誤處理器來打開文件。

with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
    data = f.read()

# make changes to the string 'data'

with open(fname + '.new', 'w',
          encoding="ascii", errors="surrogateescape") as f:
    f.write(data)

surrogateescape 錯誤處理器將所有非 ASCII bytes 解碼為 Unicode 編碼點。這些秘密編碼點會變回同樣的 bytes 當使用 surrogateescape 編碼數據並寫出的時候。
-- 來自 https://docs.python.org/3/howto/unicode.html#files-in-an-unknown-encoding

假設文件只有一種編碼,那麽可以嘗試使用 所有標準編碼 進行解碼,從解碼沒有報錯的結果中挑選出合適的。合適的結果指沒有亂碼的結果。

Python 編碼(一)— Python3