字符串和編碼


字符串和編碼

標簽(空格分隔): Python


1、歷史

1.1 字符串

字符串:是一種數據類型,但是字符串比較特殊的還有一個就是編碼問題。

1.2 bit和byte

bit是比特,byte是字節

\[8 bit = 1 byte \]

\[1024 byte = 1 KB \]

因為計算機只能處理數字,要處理文本,就必須把文本裝換成數字才能處理,最早的計算機載機設計時采用\(8\)\(bit\)作為一個字節,所以,一個字節能表示的最大整數就是\(255\),其基本單位是字節,所以需要表示更大的數據的時候就需要兩個字節,也就是\(65535\),4個字節的話表示的最大整數是\(4294967295\)。這個時候就有小精靈鬼提出疑問了,我記得C里面int是四個字節,但是最大能表示的數據大概是\(21\)億呀,這個機組有講第一位作為標識符,有正負標准,所以最大是\(21\)億,負數最大是負\(21\)億。(應該是這樣表達的吧? 提前說了是負數的話 大指的是單純的數據大小)

  • 存儲和網速的單位,無論是B還是b,代表的都是Byte
  • 帶寬的單位,無論是B還是b,代表的都是比特bit

但是我們在實際應用中更偏向於第一種計量單位,所以辦完寬帶之后咱們總感覺網速沒有那么快,實際上的網速都是\(\frac{1}{8}\)

2 解決方案

2.1 各種奇葩編碼

由於計算機是美國人發明的,因此我們了解的ASCII編碼只有\(127\)個字符被收錄進去,也就是大小寫字母、數字、符號。

但是如果要處理中文的話是遠遠不夠的,《現代漢語常用字表1988年版》就有3500個漢字而且不能和ASCII沖突,並且根據上面說的最小計量單位為byte的話,我們至少需要兩個字節,所以中國制定了GB2313編碼,用來將中文編入計算機。

但是世界上有很多語言,各國都以這種方法編碼的話,機會不可避免的產生沖突,結果就是在多語言混合文本中顯示出來會有亂碼的情況(學過C的都應該見過 錕斤拷 燙燙燙, 俗稱 手持兩把錕斤拷 口中直呼燙燙燙)。詳情鏈接

由此Unicode字符集應運而生,他將所有的語言統一到一套編碼里面,這樣就不會有亂碼問題了。其中最常見的是UCS-16編碼,用兩個字節表示一個字符(如果要用到非常偏僻的字符,就需要4個字節)。現代操作系統和大多數編程語言都直接支持UnicodeASCII用的是一個字節,Unicode一般情況下是兩個字節,特別生僻的詞會用到四個字節。

  • ASCII中字符A的編碼是十進制的65,二進制的0100 0001
  • Unicode中字符A的二進制編碼是0000 0000 0100 0001

由上可得:如果統一為Unicode編碼的話 會造成很大的內存空間浪費(以前計算機內存很小程序要精打細算,魂斗羅128k,需要實現那么多的劇情和場景,這一塊可以聯系享元設計模式一塊看),這樣的話就需要新的解決方法了。

2.2 解決方案

新的風暴已經出現,怎么能夠停滯不前。新的方案出來了也就是門外漢也聽過的UTF-8編碼(希望你們可以去看一下Mysql字符串度設置為多長合適,提示: Mysql5 是個分界線.)

UTF-8是可變長編碼,他將Unicode字符根據不同的數字代銷分為1-6個字節,常用的英文字符為1個字節,漢字是三個字節,只有很偏僻的字符才是4-6個字節、 好的,這個時候就開始擔心 如果這樣操作的話會不會時間復雜度比較高? 這個我有想過,你們也可以去搜一下,這種疑問的習慣很不錯,但是小心陷入局部技術陷阱

由上可得:大量只支持ASCII編碼的歷史遺留軟件可以在UTF-8編碼格式下正常解析。

在目前計算機內存當中,統一使用Unicode編碼,當需要保存到硬盤或者需要傳輸的時候,就轉換為UTF-8編碼。

在用記事本進行編輯的時候,從文件中讀取的UTF-8字符被轉換為Unicode放到內存當中,編輯完畢之后,保存的時候再把Unicode轉換為UTF-8進行保存。

2.3 閑扯

其實在工作當中一般情況下遇不到這種編碼問題,只有在寫爬蟲的時候可能會遇到這種問題。

但是對於Python程序員來說特別是以前的2, 這就很難受了。

在最新的Python3中,字符串是以Unicode編碼的,也就是說,Python的字符串支持多種語言,並且現在基本上也沒那么多編碼問題了。

對於單個字符的編碼,Python提供了ord()函數來獲取字符的整數表示,chr()函數把編碼轉換為對應的字符:

>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'

知道中文的整數編碼,還可以用十六進制這樣編寫str:

>>> '\u4e2d\u6587'
'中文'

Python中字符串類型是str,在內存中用Unicode表示,一個字符對應若干個字節,如果要在網絡上傳輸,或者保存到磁盤上就需要str變為字節為單位的bytes.

  • pythonbytes類型的數據用帶'b'的前綴表示。
a = "asd"

a
Out[41]: 'asd'

b = b"asd"

b
Out[43]: b'asd'

要注意的是兩者雖然表示的內容一樣,但是第二個每個字符只占用一個字節。
Unicode表示的str通過encode()方法可以編碼為制定的bytes

>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
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)

純英文的str可以用ASCII編碼為bytes,內容是一樣的,因為英文字符ASCII也就只占一個字符,只有在被編碼為Unicode的時候要占兩個字節,前面需要補0。

含有中文的str可以用UTF-8編碼會bytes。當然中文無法轉ASCII

當然我們在磁盤或者網絡中獲取字節流之后,讀取到的數據是bytes,要把bytes變為str,就要用decode()方法:

>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

如果bytes中包含無法解碼的字節,decode()方法會報錯。

>>> b'\xe4\xb8\xad\xff'.decode('utf-8')
Traceback (most recent call last):
  ...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 3: invalid start byte

如果bytes中只有一部分無效的字節,可以傳入errors='ignore'忽略錯誤的字節

>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'

len()方法用於計算str包含多少個字符

>>> len('ABC')
3
>>> len('中文')
2

如果將其轉化為bytes的話,len()函數就計算字節數

>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6

在操作字符串時,我們經常遇到strbytes的互相轉換。為了避免亂碼問題,應當始終堅持使用UTF-8編碼對strbytes進行轉換。

由於Python源代碼也是一個文本文件,所以,當你的源代碼中包含中文的時候,在保存源代碼時,就需要務必指定保存為UTF-8編碼。當Python解釋器讀取源代碼時,為了讓它按UTF-8編碼讀取,我們通常在文件開頭寫上這兩行:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

第一行注釋是為了告訴Linux/OS X系統,這是一個Python可執行程序,Windows系統會忽略這個注釋;Windows是通過文件名后綴辨識文件類型的,例如exetxt。 但是基於Unix的系統是通過文件內容辨識文件類型的。所以需要加上第一行的代碼。

第二行注釋是為了告訴Python解釋器,按照UTF-8編碼讀取源代碼,否則,你在源代碼中寫的中文輸出可能會有亂碼。但是在編譯器中也需要設置為UTF-8編碼

申明了UTF-8編碼並不意味着你的.py文件就是UTF-8編碼的,必須並且要確保文本編輯器正在使用UTF-8 without BOM編碼:

2.4

Java 解碼

在解碼之前,先把你們的idea編碼格式設置一下。 設置方法在這里


try {
    BufferedReader reader = new BufferedReader(new InputStreamReader(getMethod.getResponseBodyAsStream(), StandardCharsets.ISO_8859_1));

    String tmp = null;
    StringBuilder htmlRet = new StringBuilder();
    while ((tmp = reader.readLine()) != null) {
        htmlRet.append(tmp).append("\r\n");
    }
    System.out.println(new String(htmlRet.toString().getBytes(StandardCharsets.ISO_8859_1), "GB2312"));  // 還是亂碼的話,可以將"GB2312" 改成"UTF-8試試"
} catch (IOException e) {
    e.printStackTrace();
} finally {
    getMethod.releaseConnection();
    System.out.println(getMethod.getResponseCharSet());
}

主要參考:

  • 文中可點擊鏈接

  • 廖雪峰網站

    其實我認為生活是痛苦的,絕大多數時間是痛苦的,痛苦來自於各方各面,無時無刻不提醒着你。
    痛苦更來自於我見過那些美好的事物,如果我沒有見過陽光,我就不會知道我活在黑暗里面。

    痛苦和希望一直在正反兩面反復的催促着我,讓我穿越更多的痛苦,逐漸麻痹,去尋找希望,去變得更快樂。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM