ch3 review

This commit is contained in:
chai2010 2015-12-28 08:20:58 +08:00
parent 6fe85de585
commit 33480e55d6
5 changed files with 51 additions and 56 deletions

View File

@ -1,7 +1,6 @@
### 3.5.1. 字符串面值 ### 3.5.1. 字符串面值
字符串值也可以用字符串面值方式編寫,隻要將一繫列字節序列包含在雙引號卽可:
字符串值也可以用字符串面值方式編寫, 隻要將一繫列字節序列包含在雙引號卽可:
``` ```
"Hello, 世界" "Hello, 世界"
@ -9,9 +8,9 @@
![](../images/ch3-04.png) ![](../images/ch3-04.png)
因爲Go語言源文件總是用UTF8編碼, 併且Go的文本字符串也以UTF8編碼的方式處理, 我們可以將Unicode碼點也寫到字符串面值中. 因爲Go語言源文件總是用UTF8編碼併且Go語言的文本字符串也以UTF8編碼的方式處理因此我們可以將Unicode碼點也寫到字符串面值中。
在一個雙引號包含的字符串面值中, 可以用以反斜槓\開頭的轉義序列插入任意的數據. 下面換行, 迴車和 製表符等常見的ASCII控製代碼的轉義方式: 在一個雙引號包含的字符串面值中,可以用以反斜槓`\`開頭的轉義序列插入任意的數據。下面的換行、迴車和製表符等是常見的ASCII控製代碼的轉義方式
``` ```
\a 響鈴 \a 響鈴
@ -26,11 +25,11 @@
\\ 反斜槓 \\ 反斜槓
``` ```
可以通過十六進製或八進製轉義在字符串面值包含任意的字節. 一個十六進製的轉義是 \xhh, 其中兩個h表示十六進製數字(大寫或小寫都可以). 一個八進製轉義是 \ooo, 包含三個八進製的o數字(0到7), 但是不能超過\377. 每一個單一的字節表達一個特定的值. 稍後我們將看到如何將一個Unicode碼點寫到字符串面值中. 可以通過十六進製或八進製轉義在字符串面值包含任意的字節。一個十六進製的轉義形式是\xhh其中兩個h表示十六進製數字大寫或小寫都可以。一個八進製轉義形式是\ooo包含三個八進製的o數字0到7但是不能超過`\377`譯註對應一個字節的范圍十進製爲255。每一個單一的字節表達一個特定的值。稍後我們將看到如何將一個Unicode碼點寫到字符串面值中。
一個原生的字符串面值形式是 `...`, 使用反引號 ``` 代替雙引號. 在原生的字符串面值中, 沒有轉義操作; 全部的內容都是字面的意思, 包含退格和換行, 因此一個程序中的原生字符串面值可能跨越多行. 唯一的特殊處理是是刪除迴車以保證在所有平台上的值都是一樣的, 包括那些把迴車也放入文本文件的繫統. 一個原生的字符串面值形式是`...`,使用反引號```代替雙引號。在原生的字符串面值中,沒有轉義操作;全部的內容都是字面的意思,包含退格和換行,因此一個程序中的原生字符串面值可能跨越多行(譯註:在原生字符串面值內部是無法直接寫```字符的,可以用八進製或十六進製轉義或+"```"鏈接字符串常量完成。唯一的特殊處理是會刪除迴車以保證在所有平台上的值都是一樣的包括那些把迴車也放入文本文件的繫統譯註Windows繫統會把迴車和換行一起放入文本文件中
原生字符串面值用於編寫正則表達式會很方便, 因爲正則表達式往往會包含很多反斜槓. 原生字符串面值同時廣泛應用於HTML模闆, JSON面值, 命令行提示信息, 以及那些需要擴展到多行的場景. 原生字符串面值用於編寫正則表達式會很方便因爲正則表達式往往會包含很多反斜槓。原生字符串面值同時被廣泛應用於HTML模闆、JSON面值、命令行提示信息以及那些需要擴展到多行的場景。
```Go ```Go
const GoUsage = `Go is a tool for managing Go source code. const GoUsage = `Go is a tool for managing Go source code.

View File

@ -1,13 +1,12 @@
### 3.5.2. Unicode ### 3.5.2. Unicode
在很久以前世界还是比較簡單的起碼計算機世界就隻有一個ASCII字符集美国信息交換標準代碼。ASCII更準確地説是美国的ASCII使用7bit來表示128個字符包含英文字母的大小寫、數字、各種標點符號和設置控製符。對於早期的計算機程序来说這些就足夠了但是這也導致了世界上很多其他地區的用戶無法直接使用自己的符号繫統。隨着互聯網的發展混合多種語言的數據變得很常見译注比如本身的英文原文或中文翻译都包含了ASCII、中文、日文等多种语言字符。如何有效處理這些包含了各種語言的豐富多樣的文本數據呢
在很久以前, 世界比較簡單的, 起碼計算機就隻有一個ASCII字符集: 美国信息交換標準代碼. ASCII, 更準確地説是美国的ASCII, 使用 7bit 來表示 128 個字符: 包含英文字母的大小寫, 數字, 各種標點符號和設置控製符. 對於早期的計算機程序, 這些足夠了, 但是這也導致了世界上很多其他地區的用戶無法直接使用自己的書寫繫統. 隨着互聯網的發展, 混合多種語言的數據變了很常見. 如何有效處理這些包含了各種語言的豐富多樣的數據呢? 答案就是使用Unicode http://unicode.org 它收集了這個世界上所有的符号繫統包括重音符號和其它變音符號製表符和迴車符還有很多神祕的符號每個符號都分配一個唯一的Unicode碼點Unicode碼點對應Go語言中的rune整数類型译注rune是int32等价类型
答案就是使用Unicode(unicode.org), 它收集了這個世界上所有的書寫繫統, 包括重音符號和其他變音符號, 製表符和迴車符, 還有很多神祕符號, 每個符號都分配一個Unicode碼點, Unicode碼點對應Go語言中的rune類型. 在第八版本的Unicode標準收集了超過120,000個字符涵蓋超過100多種語言。這些在計算機程序和數據中是如何體現的呢通用的表示一個Unicode碼點的數據類型是int32也就是Go語言中rune對應的類型它的同義詞rune符文正是這個意思。
第八版本的Unicode標準收集了超過120,000個字符, 涵蓋超過100種語言. 這些在計算機程序和數據中是如何體現的那? 通用的表示一個Unicode碼點的數據類型是int32, 也就是Go語言中rune對應的類型; 它的同義詞rune符文正是這個意思. 我們可以將一個符文序列表示爲一個int32序列。這種編碼方式叫UTF-32或UCS-4每個Unicode碼點都使用同樣的大小32bit來表示。這種方式比較簡單統一但是它會浪費很多存儲空間因爲大數據計算機可讀的文本是ASCII字符本來每個ASCII字符隻需要8bit或1字節就能表示。而且卽使是常用的字符也遠少於65,536個也就是説用16bit編碼方式就能表達常用字符。但是還有其它更好的編碼方法嗎
我們可以將一個符文序列表示爲一個int32序列. 這種編碼方式叫UTF-32或UCS-4, 每個Unicode碼點都使用同樣的大小32bit來表示. 這種方式比較簡單統一, 它會浪費很多存儲空間, 因爲大數據計算機可讀的文本是ASCII字符, 本來每個ASCII字符隻需要8bit或1字節就能表示. 卽使是常用的字符也遠少於65,536個, 也就是説用16bit編碼方式就能表達常用字符. 但是, 還有更好的編碼方法嗎?

View File

@ -1,7 +1,6 @@
### 3.5.3. UTF-8 ### 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編碼兼容. 如果第一個字節的高端bit是110, 則説明需要2個字節; 後續的每個高端bit都以10開頭. 更大的Unicode碼點也是采用類似的策略處理.
``` ```
0xxxxxxx runes 0-127 (ASCII) 0xxxxxxx runes 0-127 (ASCII)
@ -10,11 +9,11 @@ UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼. UTF8編碼由
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused) 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused)
``` ```
變長的編碼無法直接通過索引來訪問第n個字符, 但是UTF8穫得了很多額外的優點. 首先UTF8編碼比較緊湊, 兼容ASCII, 併且可以自動同步: 它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置. 它也是一個前綴編碼, 所以當從左向右解碼時不會有任何歧義也併不需要向前査看. 沒有任何字符的編碼是其它字符編碼的子串, 或是其它編碼序列的字串, 因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可, 不用擔心前後的上下文會對蒐索結果産生榦擾. 同時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編碼。例如下面的字母串面值都表示相同的值
``` ```
"世界" "世界"
@ -23,17 +22,17 @@ Go的源文件采用UTF8編碼, 併且Go處理UTF8編碼的文本也很出色. u
"\U00004e16\U0000754c" "\U00004e16\U0000754c"
``` ```
上面三個轉義序列爲第一個字符串提供替代寫法, 但是它們的值都是相同的. 上面三個轉義序列都爲第一個字符串提供替代寫法,但是它們的值都是相同的。
Unicode轉義也可以使用在rune字符中. 下面三個字符是等價的: Unicode轉義也可以使用在rune字符中。下面三個字符是等價的:
``` ```
'世' '\u4e16' '\U00004e16' '世' '\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 ```Go
func HasPrefix(s, prefix string) bool { func HasPrefix(s, prefix string) bool {
@ -41,7 +40,7 @@ func HasPrefix(s, prefix string) bool {
} }
``` ```
或者是後綴測試: 或者是後綴測試
```Go ```Go
func HasSuffix(s, suffix string) bool { func HasSuffix(s, suffix string) bool {
@ -49,7 +48,7 @@ func HasSuffix(s, suffix string) bool {
} }
``` ```
或者是包含子串測試: 或者是包含子串測試
```Go ```Go
func Contains(s, substr string) bool { func Contains(s, substr string) bool {
@ -62,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 ```Go
import "unicode/utf8" import "unicode/utf8"
@ -74,7 +73,7 @@ fmt.Println(len(s)) // "13"
fmt.Println(utf8.RuneCountInString(s)) // "9" fmt.Println(utf8.RuneCountInString(s)) // "9"
``` ```
爲了處理這些眞實的字符, 我們需要一個UTF8解碼器. unicode/utf8 包提供了實現, 我們可以這樣使用: 爲了處理這些眞實的字符我們需要一個UTF8解碼器。unicode/utf8包提供了该功能我們可以這樣使用
```Go ```Go
for i := 0; i < len(s); { for i := 0; i < len(s); {
@ -84,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) ![](../images/ch3-05.png)
@ -94,7 +93,7 @@ for i, r := range "Hello, 世界" {
} }
``` ```
我們可以使用一個簡單的循環來統計字符串中字符的數目, 像這樣: 我們可以使用一個簡單的循環來統計字符串中字符的數目,像這樣:
```Go ```Go
n := 0 n := 0
@ -103,7 +102,7 @@ for _, _ = range s {
} }
``` ```
想其它形式的循環那樣, 我們可以忽略不需要的變量: 像其它形式的循環那樣,我們也可以忽略不需要的變量:
```Go ```Go
n := 0 n := 0
@ -112,15 +111,15 @@ for range s {
} }
``` ```
或者我們可以直接調用 utf8.RuneCountInString(s) 函數. 或者我們可以直接調用utf8.RuneCountInString(s)函數。
正如我們前面提到了, 文本字符串采用UTF8編碼隻是一種慣例,但是對於循環的眞正字符串併不是一個慣例, 這是正確的. 如果用於循環的字符串隻是一個普通的二進製數據, 或者是含有錯誤編碼的UTF8數據, 將會發送什麽? 正如我們前面提到文本字符串采用UTF8編碼隻是一種慣例但是對於循環的眞正字符串併不是一個慣例這是正確的。如果用於循環的字符串隻是一個普通的二進製數據或者是含有錯誤編碼的UTF8數據將會發送什麽呢
每一個UTF8字符解碼, 不管是顯示地調用 utf8.DecodeRuneInString 解碼或在 range 循環中隱式地解碼, 如果遇到一個錯誤的輸入字節, 將生成一個特别的Unicode字符 '\uFFFD', 在印刷中這個符號通常是一個黑色六角或鑽石形狀, 里面包含一個白色的問號(?). 當程序遇到這樣的一個字符, 通常是一個信號, 説明輸入併不是一個完美沒有錯誤的的UTF8編碼字符串. 每一個UTF8字符解碼不管是顯式地調用utf8.DecodeRuneInString解碼或是在range循環中隱式地解碼如果遇到一個錯誤的UTF8编码輸入將生成一個特别的Unicode字符'\uFFFD',在印刷中這個符號通常是一個黑色六角或鑽石形狀,里面包含一個白色的問號(?。當程序遇到這樣的一個字符通常是一個危险信號説明輸入併不是一個完美沒有錯誤的UTF8字符串。
UTF8作爲交換格式是非常方便的, 但是在程序內部采用rune類型可能更方便, 因爲rune大小一致, 支持數組索引和方便切割. UTF8字符串作爲交換格式是非常方便的但是在程序內部采用rune序列可能更方便因爲rune大小一致支持數組索引和方便切割。
string 接受到 []rune 的轉換, 可以將一個UTF8編碼的字符串解碼爲Unicode字符序列: string接受到[]rune的类型轉換可以將一個UTF8編碼的字符串解碼爲Unicode字符序列
```Go ```Go
// "program" in Japanese katakana // "program" in Japanese katakana
@ -130,22 +129,22 @@ r := []rune(s)
fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]" fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]"
``` ```
(在第一個Printf中的 `% x` 參數用於在每個十六進製數字前插入一個空格.) 在第一個Printf中的`% x`參數用於在每個十六進製數字前插入一個空格。)
如果是將一個 []rune 類型的Unicode字符切片或數組轉爲string, 則對它們進行UTF8編碼: 如果是將一個[]rune類型的Unicode字符slice或數組轉爲string則對它們進行UTF8編碼
```Go ```Go
fmt.Println(string(r)) // "プログラム" fmt.Println(string(r)) // "プログラム"
``` ```
將一個整數轉型爲字符串意思是生成整數作爲Unicode碼點的UTF8編碼的字符串: 將一個整數轉型爲字符串意思是生成以只包含对应Unicode碼點字符的UTF8字符串
```Go ```Go
fmt.Println(string(65)) // "A", not "65" fmt.Println(string(65)) // "A", not "65"
fmt.Println(string(0x4eac)) // "京" fmt.Println(string(0x4eac)) // "京"
``` ```
如果對應碼點的字符是無效的, 則用'\uFFFD'無效字符作爲替換: 如果對應碼點的字符是無效的,則用'\uFFFD'無效字符作爲替換:
```Go ```Go
fmt.Println(string(1234567)) // "(?)" fmt.Println(string(1234567)) // "(?)"

View File

@ -1,11 +1,10 @@
### 3.5.4. 字符串和Byte切片 ### 3.5.4. 字符串和Byte切片
標準庫中有四個包對字符串處理尤爲重要bytes、strings、strconv和unicode包。strings包提供了許多如字符串的査詢、替換、比較、截斷、拆分和合併等功能。
標準庫中有四個包對字符串處理尤爲重要: bytes, strings, strconv, 和 unicode. strings 包提供了許多如字符串的査詢, 替換, 比較, 截斷, 拆分, 和合併等功能. bytes包也提供了很多類似功能的函數但是針對和字符串有着相同結構的[]byte類型。因爲字符串是隻讀的因此逐步構建字符串會導致很多分配和複製。在這種情況下使用bytes.Buffer類型将會更有效稍後我們將展示。
bytes 包也提供了很多類似功能的函數, 但是針對和字符串有着相同結構的 []byte 類型. 因爲字符串是隻讀的, 因此逐步構建字符串會導致很多分配和複製. 在這種情況下, 使用 bytes.Buffer 類型會更有效, 稍後我們將展示. strconv包提供了布爾型、整型數、浮點數和對應字符串間的相互轉換, 還提供了雙引號的字符串面值形式的轉換.
strconv 包提供了 布爾型, 整型數, 浮點數和對應字符串間的相互轉換, 還提供了雙引號的字符串面值形式的轉換.
unicode 包提供了類似 IsDigit, IsLetter, IsUpper, 和 IsLower 等功能, 它們用於給字符分類. 每個函數有一個單一的rune類型的參數, 然後返迴一個布爾值. 像 ToUpper 和 ToLower 之類的轉換函數將用於rune字符的大小寫轉換. 所有的這些函數都是遵循Unicode標準定義的字母,數字等分類規范. strings 包也有類似的函數, 它們是 ToUpper 和 ToLower, 將原始字符串的每個字符都做相應的轉換, 然後返迴新的字符串. unicode 包提供了類似 IsDigit, IsLetter, IsUpper, 和 IsLower 等功能, 它們用於給字符分類. 每個函數有一個單一的rune類型的參數, 然後返迴一個布爾值. 像 ToUpper 和 ToLower 之類的轉換函數將用於rune字符的大小寫轉換. 所有的這些函數都是遵循Unicode標準定義的字母,數字等分類規范. strings 包也有類似的函數, 它們是 ToUpper 和 ToLower, 將原始字符串的每個字符都做相應的轉換, 然後返迴新的字符串.

View File

@ -1,8 +1,8 @@
## 3.5. 字符串 ## 3.5. 字符串
一個字符串是一個不可改變的字節序列. 字符串可以包含任意的數據, 包括字節值0, 但是通常包含人類可讀的文本. 文本字符串通常被解釋爲采用UTF8編碼的Unicode碼點(rune)序列, 我們稍後會詳細討論這個問題. 一個字符串是一個不可改變的字節序列。字符串可以包含任意的數據包括byte值0但是通常是用來包含人類可讀的文本。文本字符串通常被解釋爲采用UTF8編碼的Unicode碼點rune序列我們稍後會詳細討論這個問題。
內置的 len 函數可以返迴一個字符串的字節數目(不是rune字符數目), 索引操作 s[i] 返迴第i個字節的字節值, i 必鬚滿足 0 ≤ i< len(s) 條件約束. 內置的len函數可以返迴一個字符串中的字節數目不是rune字符數目索引操作s[i]返迴第i個字節的字節值i必鬚滿足0 ≤ i< len(s)條件約束
```Go ```Go
s := "hello, world" s := "hello, world"
@ -10,23 +10,23 @@ fmt.Println(len(s)) // "12"
fmt.Println(s[0], s[7]) // "104 119" ('h' and 'w') fmt.Println(s[0], s[7]) // "104 119" ('h' and 'w')
``` ```
如果視圖訪問超出字符串范圍的字節將會導致panic異常: 如果試圖訪問超出字符串索引范圍的字節將會導致panic異常
```Go ```Go
c := s[len(s)] // panic: index out of range c := s[len(s)] // panic: index out of range
``` ```
第i個字節併不一定是字符串的第i個字符, 因此對於非ASCII字符的UTF8編碼會要兩個或多個字節. 我們簡單説下字符的工作方式. 第i個字節併不一定是字符串的第i個字符因爲對於非ASCII字符的UTF8編碼會要兩個或多個字節。我們先簡單説下字符的工作方式。
子字符串操作s[i:j]基於原始的s字符串的第i個字節開始到第j個字節(併不包含j本身)生成一個新字符串. 生成的子字符串將包含 j-i 個字節. 子字符串操作s[i:j]基於原始的s字符串的第i個字節開始到第j個字節併不包含j本身生成一個新字符串。生成的新字符串將包含j-i個字節。
```Go ```Go
fmt.Println(s[0:5]) // "hello" fmt.Println(s[0:5]) // "hello"
``` ```
同樣, 如果索引超出字符串范圍或者j小於i的話將導致panic異常. 同樣如果索引超出字符串范圍或者j小於i的話將導致panic異常。
不管i還是j都可能被忽略, 當它們被忽略時將采用0作爲開始位置, 采用 len(s) 作爲接受的位置. 不管i還是j都可能被忽略當它們被忽略時將采用0作爲開始位置采用len(s)作爲結束的位置。
```Go ```Go
fmt.Println(s[:5]) // "hello" fmt.Println(s[:5]) // "hello"
@ -34,16 +34,15 @@ fmt.Println(s[7:]) // "world"
fmt.Println(s[:]) // "hello, world" fmt.Println(s[:]) // "hello, world"
``` ```
其中 + 操作符將兩個字符串鏈接構造一個新字符串: 其中+操作符將兩個字符串鏈接構造一個新字符串
```Go ```Go
fmt.Println("goodbye" + s[5:]) // "goodbye, world" fmt.Println("goodbye" + s[5:]) // "goodbye, world"
``` ```
字符串可以用 == 和 < 進行比較; 比較通過逐個字節比較完成的, 因此比較的結果是字符串自然編碼的順序. 字符串可以用==和<進行比較比較通過逐個字節比較完成的因此比較的結果是字符串自然編碼的順序
字符串的值是不可變的:一個字符串包含的字節序列永遠不會被改變,當然我們也可以給一個字符串變量分配一個新字符串值。可以像下面這樣將一個字符串追加到另一個字符串:
字符串的值是不可變的: 一個字符串包含的字節序列永遠不會被改變, 當然我們也可以給一個字符串變量分配一個新字符串值. 可以像下面這樣將一個字符串追加到另一個字符串
```Go ```Go
s := "left foot" s := "left foot"
@ -51,20 +50,20 @@ t := s
s += ", right foot" s += ", right foot"
``` ```
這併不會導致原始的字符串值被改變, 但是 s 將因爲 += 語句持有一個新的字符串值, 但是 t 依然是包含原先的字符串值. 這併不會導致原始的字符串值被改變但是變量s將因爲+=語句持有一個新的字符串值但是t依然是包含原先的字符串值。
```Go ```Go
fmt.Println(s) // "left foot, right foot" fmt.Println(s) // "left foot, right foot"
fmt.Println(t) // "left foot" fmt.Println(t) // "left foot"
``` ```
因爲字符串是不可脩改的, 因此嚐試脩改字符串內部數據的操作是被禁止的: 因爲字符串是不可脩改的,因此嚐試脩改字符串內部數據的操作也是被禁止的:
```Go ```Go
s[0] = 'L' // compile error: cannot assign to s[0] s[0] = 'L' // compile error: cannot assign to s[0]
``` ```
不變性意味如果兩個字符串共享相同的底層數據是安全的, 這使得複製任何長度的字符串代價是低廉的. 同樣, 一個字符串 s 和對應的子字符串 s[7:] 也可以安全地共享相同的內存, 因此字符串切片操作代價也是低廉的. 在這兩種情況下都沒有必要分配新的內存. 圖3.4 演示了一個字符串和兩個字串共享相同的底層數據. 不變性意味如果兩個字符串共享相同的底層數據的話也是安全的這使得複製任何長度的字符串代價是低廉的。同樣一個字符串s和對應的子字符串切片s[7:]的操作也可以安全地共享相同的內存,因此字符串切片操作代價也是低廉的。在這兩種情況下都沒有必要分配新的內存。 圖3.4演示了一個字符串和兩個字串共享相同的底層數據。
{% include "./ch3-05-1.md" %} {% include "./ch3-05-1.md" %}