mirror of
				https://github.com/gopl-zh/gopl-zh.github.com.git
				synced 2025-10-25 07:42:16 +00:00 
			
		
		
		
	回到简体
This commit is contained in:
		| @@ -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编码。例如:下面的字母串面值都表示相同的值: | ||||
|  | ||||
| ``` | ||||
| "世界" | ||||
| @@ -22,17 +22,17 @@ Go語言的源文件采用UTF8編碼,併且Go語言處理UTF8編碼的文本 | ||||
| "\U00004e16\U0000754c" | ||||
| ``` | ||||
|  | ||||
| 上面三個轉義序列都爲第一個字符串提供替代寫法,但是它們的值都是相同的。 | ||||
| 上面三个转义序列都为第一个字符串提供替代写法,但是它们的值都是相同的。 | ||||
|  | ||||
| Unicode轉義也可以使用在rune字符中。下面三個字符是等價的: | ||||
| Unicode转义也可以使用在rune字符中。下面三个字符是等价的: | ||||
|  | ||||
| ``` | ||||
| '世' '\u4e16' '\U00004e16' | ||||
| ``` | ||||
|  | ||||
| 對於小於256碼點值可以寫在一個十六進製轉義字節中,例如'\x41'對應字符'A',但是對於更大的碼點則必須使用\u或\U轉義形式。因此,'\xe4\xb8\x96'併不是一個合法的rune字符,雖然這三個字節對應一個有效的UTF8編碼的碼點。 | ||||
| 对于小于256码点值可以写在一个十六进制转义字节中,例如'\x41'对应字符'A',但是对于更大的码点则必须使用\u或\U转义形式。因此,'\xe4\xb8\x96'并不是一个合法的rune字符,虽然这三个字节对应一个有效的UTF8编码的码点。 | ||||
|  | ||||
| 得益於UTF8編碼優良的設計,諸多字符串操作都不需要解碼操作。我們可以不用解碼直接測試一個字符串是否是另一個字符串的前綴: | ||||
| 得益于UTF8编码优良的设计,诸多字符串操作都不需要解码操作。我们可以不用解码直接测试一个字符串是否是另一个字符串的前缀: | ||||
|  | ||||
| ```Go | ||||
| func HasPrefix(s, prefix string) bool { | ||||
| @@ -40,7 +40,7 @@ func HasPrefix(s, prefix string) bool { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| 或者是後綴測試: | ||||
| 或者是后缀测试: | ||||
|  | ||||
| ```Go | ||||
| func HasSuffix(s, suffix string) bool { | ||||
| @@ -48,7 +48,7 @@ func HasSuffix(s, suffix string) bool { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| 或者是包含子串測試: | ||||
| 或者是包含子串测试: | ||||
|  | ||||
| ```Go | ||||
| func Contains(s, substr 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个字节。 | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -93,7 +93,7 @@ for i, r := range "Hello, 世界" { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| 我們可以使用一個簡單的循環來統計字符串中字符的數目,像這樣: | ||||
| 我们可以使用一个简单的循环来统计字符串中字符的数目,像这样: | ||||
|  | ||||
| ```Go | ||||
| n := 0 | ||||
| @@ -102,7 +102,7 @@ for _, _ = range s { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| 像其它形式的循環那樣,我們也可以忽略不需要的變量: | ||||
| 像其它形式的循环那样,我们也可以忽略不需要的变量: | ||||
|  | ||||
| ```Go | ||||
| n := 0 | ||||
| @@ -111,15 +111,15 @@ for range s { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| 或者我們可以直接調用utf8.RuneCountInString(s)函數。 | ||||
| 或者我们可以直接调用utf8.RuneCountInString(s)函数。 | ||||
|  | ||||
| 正如我們前面提到的,文本字符串采用UTF8編碼隻是一種慣例,但是對於循環的眞正字符串併不是一個慣例,這是正確的。如果用於循環的字符串隻是一個普通的二進製數據,或者是含有錯誤編碼的UTF8數據,將會發送什麽呢? | ||||
| 正如我们前面提到的,文本字符串采用UTF8编码只是一种惯例,但是对于循环的真正字符串并不是一个惯例,这是正确的。如果用于循环的字符串只是一个普通的二进制数据,或者是含有错误编码的UTF8数据,将会发送什么呢? | ||||
|  | ||||
| 每一個UTF8字符解碼,不管是顯式地調用utf8.DecodeRuneInString解碼或是在range循環中隱式地解碼,如果遇到一個錯誤的UTF8編碼輸入,將生成一個特别的Unicode字符'\uFFFD',在印刷中這個符號通常是一個黑色六角或鑽石形狀,里面包含一個白色的問號"<22>"。當程序遇到這樣的一個字符,通常是一個危險信號,説明輸入併不是一個完美沒有錯誤的UTF8字符串。 | ||||
| 每一个UTF8字符解码,不管是显式地调用utf8.DecodeRuneInString解码或是在range循环中隐式地解码,如果遇到一个错误的UTF8编码输入,将生成一个特别的Unicode字符'\uFFFD',在印刷中这个符号通常是一个黑色六角或钻石形状,里面包含一个白色的问号"<22>"。当程序遇到这样的一个字符,通常是一个危险信号,说明输入并不是一个完美没有错误的UTF8字符串。 | ||||
|  | ||||
| UTF8字符串作爲交換格式是非常方便的,但是在程序內部采用rune序列可能更方便,因爲rune大小一致,支持數組索引和方便切割。 | ||||
| UTF8字符串作为交换格式是非常方便的,但是在程序内部采用rune序列可能更方便,因为rune大小一致,支持数组索引和方便切割。 | ||||
|  | ||||
| string接受到[]rune的類型轉換,可以將一個UTF8編碼的字符串解碼爲Unicode字符序列: | ||||
| string接受到[]rune的类型转换,可以将一个UTF8编码的字符串解码为Unicode字符序列: | ||||
|  | ||||
| ```Go | ||||
| // "program" in Japanese katakana | ||||
| @@ -129,22 +129,22 @@ r := []rune(s) | ||||
| fmt.Printf("%x\n", r)  // "[30d7 30ed 30b0 30e9 30e0]" | ||||
| ``` | ||||
|  | ||||
| (在第一個Printf中的`% x`參數用於在每個十六進製數字前插入一個空格。) | ||||
| (在第一个Printf中的`% x`参数用于在每个十六进制数字前插入一个空格。) | ||||
|  | ||||
| 如果是將一個[]rune類型的Unicode字符slice或數組轉爲string,則對它們進行UTF8編碼: | ||||
| 如果是将一个[]rune类型的Unicode字符slice或数组转为string,则对它们进行UTF8编码: | ||||
|  | ||||
| ```Go | ||||
| fmt.Println(string(r)) // "プログラム" | ||||
| ``` | ||||
|  | ||||
| 將一個整數轉型爲字符串意思是生成以隻包含對應Unicode碼點字符的UTF8字符串: | ||||
| 将一个整数转型为字符串意思是生成以只包含对应Unicode码点字符的UTF8字符串: | ||||
|  | ||||
| ```Go | ||||
| fmt.Println(string(65))     // "A", not "65" | ||||
| fmt.Println(string(0x4eac)) // "京" | ||||
| ``` | ||||
|  | ||||
| 如果對應碼點的字符是無效的,則用'\uFFFD'無效字符作爲替換: | ||||
| 如果对应码点的字符是无效的,则用'\uFFFD'无效字符作为替换: | ||||
|  | ||||
| ```Go | ||||
| fmt.Println(string(1234567)) // "<22>" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user