字符串和編碼
標簽(空格分隔): Python
1、歷史
1.1 字符串
字符串:是一種數據類型,但是字符串比較特殊的還有一個就是編碼問題。
1.2 bit和byte
bit
是比特,byte
是字節
因為計算機只能處理數字,要處理文本,就必須把文本裝換成數字才能處理,最早的計算機載機設計時采用\(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個字節)。現代操作系統和大多數編程語言都直接支持Unicode
。ASCII
用的是一個字節,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
.
python
對bytes
類型的數據用帶'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
在操作字符串時,我們經常遇到str
和bytes
的互相轉換。為了避免亂碼問題,應當始終堅持使用UTF-8
編碼對str
和bytes
進行轉換。
由於Python
源代碼也是一個文本文件,所以,當你的源代碼中包含中文的時候,在保存源代碼時,就需要務必指定保存為UTF-8
編碼。當Python
解釋器讀取源代碼時,為了讓它按UTF-8
編碼讀取,我們通常在文件開頭寫上這兩行:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行注釋是為了告訴Linux/OS X
系統,這是一個Python
可執行程序,Windows
系統會忽略這個注釋;Windows
是通過文件名后綴辨識文件類型的,例如exe
,txt
。 但是基於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());
}
主要參考:
-
文中可點擊鏈接
-
廖雪峰網站
其實我認為生活是痛苦的,絕大多數時間是痛苦的,痛苦來自於各方各面,無時無刻不提醒着你。
痛苦更來自於我見過那些美好的事物,如果我沒有見過陽光,我就不會知道我活在黑暗里面。痛苦和希望一直在正反兩面反復的催促着我,讓我穿越更多的痛苦,逐漸麻痹,去尋找希望,去變得更快樂。