ch3-05 review

This commit is contained in:
chai2010
2015-12-28 10:40:45 +08:00
parent 33480e55d6
commit 315fb47435
4 changed files with 42 additions and 45 deletions

View File

@@ -1,6 +1,6 @@
### 3.5.3. UTF-8
UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼。UTF8編碼由Go語言之父Ken Thompson和Rob Pike共同發明的現在已經是Unicode的標準。UTF8编码使用1到4個字節來表示每個Unicode碼點ASCII部分字符隻使用1個字節常用字符部分使用2或3個字節表示。每個符號編碼後第一個字節的高端bit位用於表示總共有多少编码個字節。如果第一個字節的高端bit爲0則表示對應7bit的ASCII字符ASCII字符每個字符依然是一個字節和傳統的ASCII編碼兼容。如果第一個字節的高端bit是110則説明需要2個字節後續的每個高端bit都以10開頭。更大的Unicode碼點也是采用類似的策略處理。
UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼。UTF8編碼由Go語言之父Ken Thompson和Rob Pike共同發明的現在已經是Unicode的標準。UTF8編碼使用1到4個字節來表示每個Unicode碼點ASCII部分字符隻使用1個字節常用字符部分使用2或3個字節表示。每個符號編碼後第一個字節的高端bit位用於表示總共有多少編碼個字節。如果第一個字節的高端bit爲0則表示對應7bit的ASCII字符ASCII字符每個字符依然是一個字節和傳統的ASCII編碼兼容。如果第一個字節的高端bit是110則説明需要2個字節後續的每個高端bit都以10開頭。更大的Unicode碼點也是采用類似的策略處理。
```
0xxxxxxx runes 0-127 (ASCII)
@@ -9,11 +9,11 @@ UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼。UTF8編碼
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused)
```
變長的編碼無法直接通過索引來訪問第n個字符但是UTF8编码穫得了很多額外的優點。首先UTF8編碼比較緊湊完全兼容ASCII併且可以自動同步它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置。它也是一個前綴編碼所以當從左向右解碼時不會有任何歧義也併不需要向前査看译注像GBK之类的编码,如果不知道起位置可能会出现歧义。沒有任何字符的編碼是其它字符編碼的子串或是其它編碼序列的字串因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可不用擔心前後的上下文會對蒐索結果産生榦擾。同時UTF8編碼的順序和Unicode碼點的順序一致因此可以直接排序UTF8編碼序列。同时因为沒有嵌入的NUL(0)字節可以很好地兼容那些使用NUL作爲字符串結尾的編程語言。
變長的編碼無法直接通過索引來訪問第n個字符但是UTF8編碼穫得了很多額外的優點。首先UTF8編碼比較緊湊完全兼容ASCII併且可以自動同步它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置。它也是一個前綴編碼所以當從左向右解碼時不會有任何歧義也併不需要向前査看譯註像GBK之類的編碼,如果不知道起位置可能會出現歧義。沒有任何字符的編碼是其它字符編碼的子串或是其它編碼序列的字串因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可不用擔心前後的上下文會對蒐索結果産生榦擾。同時UTF8編碼的順序和Unicode碼點的順序一致因此可以直接排序UTF8編碼序列。同時因爲沒有嵌入的NUL(0)字節可以很好地兼容那些使用NUL作爲字符串結尾的編程語言。
Go言的源文件采用UTF8編碼併且Go言處理UTF8編碼的文本也很出色。unicode包提供了諸多處理rune字符相關功能的函數比如區分字母和數組或者是字母的大寫和小寫轉換等unicode/utf8包提供了用rune字符序列的UTF8編碼和解碼的功能。
Go言的源文件采用UTF8編碼併且Go言處理UTF8編碼的文本也很出色。unicode包提供了諸多處理rune字符相關功能的函數比如區分字母和數組或者是字母的大寫和小寫轉換等unicode/utf8包提供了用rune字符序列的UTF8編碼和解碼的功能。
有很多Unicode字符很難直接從鍵盤輸入併且有很多字符有着相似的結構;有一些甚至是不可見的字符(译注中文和日文就有很多相似但不同的字。Go言字符串面值中的Unicode轉義字符讓我們可以通過Unicode碼點輸入特殊的字符。有兩種形式\uhhhh對應16bit的碼點值\Uhhhhhhhh對應32bit的碼點值其中h是一個十六進製數字一般很少需要使用32bit的形式。每一個對應碼點的UTF8編碼。例如下面的字母串面值都表示相同的值
有很多Unicode字符很難直接從鍵盤輸入併且有很多字符有着相似的結構;有一些甚至是不可見的字符(譯註中文和日文就有很多相似但不同的字。Go言字符串面值中的Unicode轉義字符讓我們可以通過Unicode碼點輸入特殊的字符。有兩種形式\uhhhh對應16bit的碼點值\Uhhhhhhhh對應32bit的碼點值其中h是一個十六進製數字一般很少需要使用32bit的形式。每一個對應碼點的UTF8編碼。例如下面的字母串面值都表示相同的值
```
"世界"
@@ -32,7 +32,7 @@ Unicode轉義也可以使用在rune字符中。下面三個字符是等價的
對於小於256碼點值可以寫在一個十六進製轉義字節中例如'\x41'對應字符'A',但是對於更大的碼點則必鬚使用\u或\U轉義形式。因此'\xe4\xb8\x96'併不是一個合法的rune字符雖然這三個字節對應一個有效的UTF8編碼的碼點。
得益於UTF8编码優良的設計,諸多字符串操作都不需要解碼操作。我們可以不用解碼直接測試一個字符串是否是另一個字符串的前綴:
得益於UTF8編碼優良的設計,諸多字符串操作都不需要解碼操作。我們可以不用解碼直接測試一個字符串是否是另一個字符串的前綴:
```Go
func HasPrefix(s, prefix string) bool {
@@ -61,9 +61,9 @@ func Contains(s, substr string) bool {
}
```
對於UTF8編碼後文本的處理和原始的字節處理邏輯是一樣的。但是對應很多其它編碼則併不是這樣的。上面的函數都來自strings字符串處理包真实的代包含了一個用哈希技術優化的Contains 實現。)
對於UTF8編碼後文本的處理和原始的字節處理邏輯是一樣的。但是對應很多其它編碼則併不是這樣的。上面的函數都來自strings字符串處理包眞實的代包含了一個用哈希技術優化的Contains 實現。)
另一方面如果我們眞的關心每個Unicode字符我們可以使用其它理方式。考慮前面的第一個例子中的字符串它包混合了中西兩種字符。圖3.5展示了它的內存表示形式。字符串包含13個字節以UTF8形式編碼但是隻對應9個Unicode字符
另一方面如果我們眞的關心每個Unicode字符我們可以使用其它理方式。考慮前面的第一個例子中的字符串它包混合了中西兩種字符。圖3.5展示了它的內存表示形式。字符串包含13個字節以UTF8形式編碼但是隻對應9個Unicode字符
```Go
import "unicode/utf8"
@@ -73,7 +73,7 @@ fmt.Println(len(s)) // "13"
fmt.Println(utf8.RuneCountInString(s)) // "9"
```
爲了處理這些眞實的字符我們需要一個UTF8解碼器。unicode/utf8包提供了功能,我們可以這樣使用:
爲了處理這些眞實的字符我們需要一個UTF8解碼器。unicode/utf8包提供了功能,我們可以這樣使用:
```Go
for i := 0; i < len(s); {
@@ -83,7 +83,7 @@ for i := 0; i < len(s); {
}
```
每一次調用DecodeRuneInString函數都返迴一個r和長度r對應字符本身長度對應r采用UTF8編碼後的编码字節數目。長度可以用於更新第i個字符在字符串中的字節索引位置。但是這種编码方式是笨拙的我們需要更簡潔的語法。幸運的是Go言的range循環在處理字符串的時候會自動隱式解碼UTF8字符串。下面的循環運行如圖3.5所示需要註意的是對於非ASCII索引更新的步長超過1個字節。
每一次調用DecodeRuneInString函數都返迴一個r和長度r對應字符本身長度對應r采用UTF8編碼後的編碼字節數目。長度可以用於更新第i個字符在字符串中的字節索引位置。但是這種編碼方式是笨拙的我們需要更簡潔的語法。幸運的是Go言的range循環在處理字符串的時候會自動隱式解碼UTF8字符串。下面的循環運行如圖3.5所示需要註意的是對於非ASCII索引更新的步長超過1個字節。
![](../images/ch3-05.png)
@@ -115,11 +115,11 @@ for range s {
正如我們前面提到的文本字符串采用UTF8編碼隻是一種慣例但是對於循環的眞正字符串併不是一個慣例這是正確的。如果用於循環的字符串隻是一個普通的二進製數據或者是含有錯誤編碼的UTF8數據將會發送什麽呢
每一個UTF8字符解碼不管是顯式地調用utf8.DecodeRuneInString解碼或是在range循環中隱式地解碼如果遇到一個錯誤的UTF8编码輸入將生成一個特别的Unicode字符'\uFFFD',在印刷中這個符號通常是一個黑色六角或鑽石形狀,里面包含一個白色的問號(?)。當程序遇到這樣的一個字符,通常是一個危信號説明輸入併不是一個完美沒有錯誤的UTF8字符串。
每一個UTF8字符解碼不管是顯式地調用utf8.DecodeRuneInString解碼或是在range循環中隱式地解碼如果遇到一個錯誤的UTF8編碼輸入將生成一個特别的Unicode字符'\uFFFD',在印刷中這個符號通常是一個黑色六角或鑽石形狀,里面包含一個白色的問號(?)。當程序遇到這樣的一個字符,通常是一個危信號説明輸入併不是一個完美沒有錯誤的UTF8字符串。
UTF8字符串作爲交換格式是非常方便的但是在程序內部采用rune序列可能更方便因爲rune大小一致支持數組索引和方便切割。
string接受到[]rune的型轉換可以將一個UTF8編碼的字符串解碼爲Unicode字符序列
string接受到[]rune的型轉換可以將一個UTF8編碼的字符串解碼爲Unicode字符序列
```Go
// "program" in Japanese katakana
@@ -137,7 +137,7 @@ fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]"
fmt.Println(string(r)) // "プログラム"
```
將一個整數轉型爲字符串意思是生成以包含对应Unicode碼點字符的UTF8字符串
將一個整數轉型爲字符串意思是生成以包含對應Unicode碼點字符的UTF8字符串
```Go
fmt.Println(string(65)) // "A", not "65"