GO語言————7.6 字串、陣列和切片的應用
7.6 字串、陣列和切片的應用
7.6.1 從字串生成位元組切片
假設 s 是一個字串(本質上是一個位元組陣列),那麼就可以直接通過 c := []byte(s)
來獲取一個位元組的切片 c。另外,您還可以通過 copy 函式來達到相同的目的:copy(dst []byte, src string)
。
同樣的,還可以使用 for-range 來獲得每個元素(Listing 7.13—for_string.go):
package main
import "fmt"
func main() {
s := "\u00ff\u754c"
for i, c := range s {
fmt.Printf("%d:%c ", i, c)
}
}
輸出:
0:ÿ 2:界
我們知道,Unicode 字元會佔用 2 個位元組,有些甚至需要 3 個或者 4 個位元組來進行表示。如果發現錯誤的 UTF8 字元,則該字元會被設定為 U+FFFD 並且索引向前移動一個位元組。和字串轉換一樣,您同樣可以使用 c := []int32(s)
語法,這樣切片中的每個 int 都會包含對應的 Unicode 程式碼,因為字串中的每次字元都會對應一個整數。類似的,您也可以將字串轉換為元素型別為 rune 的切片:r := []rune(s)
。
可以通過程式碼 len([]int32(s))
來獲得字串中字元的數量,但使用 utf8.RuneCountInString(s)
效率會更高一點。(參考count_characters.go)
您還可以將一個字串追加到某一個字元陣列的尾部:
var b []byte
var s string
b = append(b, s...)
7.6.2 獲取字串的某一部分
使用 substr := str[start:end]
可以從字串 str 獲取到從索引 start 開始到 end-1
位置的子字串。同樣的,str[start:]
則表示獲取從 start 開始到 len(str)-1
位置的子字串。而 str[:end]
表示獲取從 0 開始到 end-1
的子字串。
7.6.3 字串和切片的記憶體結構
在記憶體中,一個字串實際上是一個雙字結構,即一個指向實際資料的指標和記錄字串長度的整數(見圖 7.4)。因為指標對使用者來說是完全不可見,因此我們可以依舊把字串看做是一個值型別,也就是一個字元陣列。
字串 string s = "hello"
和子字串 t = s[2:3]
在記憶體中的結構可以用下圖表示:
7.6.4 修改字串中的某個字元
Go 語言中的字串是不可變的,也就是說 str[index]
這樣的表示式是不可以被放在等號左側的。如果嘗試執行 str[i] = 'D'
會得到錯誤:cannot assign to str[i]
。
因此,您必須先將字串轉換成位元組陣列,然後再通過修改陣列中的元素值來達到修改字串的目的,最後將位元組陣列轉換回字串格式。
例如,將字串 "hello" 轉換為 "cello":
s := "hello"
c := []byte(s)
c[0] = 'c'
s2 := string(c) // s2 == "cello"
所以,您可以通過操作切片來完成對字串的操作。
7.6.5 位元組陣列對比函式
下面的 Compare
函式會返回兩個位元組陣列字典順序的整數對比結果,即 0 if a == b, -1 if a < b, 1 if a > b
。
func Compare(a, b[]byte) int {
for i:=0; i < len(a) && i < len(b); i++ {
switch {
case a[i] > b[i]:
return 1
case a[i] < b[i]:
return -1
}
}
// 陣列的長度可能不同
switch {
case len(a) < len(b):
return -1
case len(a) > len(b):
return 1
}
return 0 // 陣列相等
}
7.6.6 搜尋及排序切片和陣列
標準庫提供了 sort
包來實現常見的搜尋和排序操作。您可以使用 sort
包中的函式 func Ints(a []int)
來實現對 int 型別的切片排序。例如 sort.Ints(arri)
,其中變數 arri 就是需要被升序排序的陣列或切片。為了檢查某個陣列是否已經被排序,可以通過函式 IntsAreSorted(a []int) bool
來檢查,如果返回 true 則表示已經被排序。
類似的,可以使用函式 func Float64s(a []float64)
來排序 float64 的元素,或使用函式 func Strings(a []string)
排序字串元素。
想要在陣列或切片中搜尋一個元素,該陣列或切片必須先被排序(因為標準庫的搜尋演算法使用的是二分法)。然後,您就可以使用函式 func SearchInts(a []int, n int) int
進行搜尋,並返回對應結果的索引值。
當然,還可以搜尋 float64 和字串:
func SearchFloat64s(a []float64, x float64) int
func SearchStrings(a []string, x string) int
您可以通過檢視 官方文件 來獲取更詳細的資訊。
這就是如何使用 sort
包的方法,我們會在第 11.6 節對它的細節進行深入,並實現一個屬於我們自己的版本。
7.6.7 append 函式常見操作
我們在第 7.5 節提到的 append 非常有用,它能夠用於各種方面的操作:
將切片 b 的元素追加到切片 a 之後:
a = append(a, b...)
複製切片 a 的元素到新的切片 b 上:
b = make([]T, len(a)) copy(b, a)
刪除位於索引 i 的元素:
a = append(a[:i], a[i+1:]...)
切除切片 a 中從索引 i 至 j 位置的元素:
a = append(a[:i], a[j:]...)
為切片 a 擴充套件 j 個元素長度:
a = append(a, make([]T, j)...)
在索引 i 的位置插入元素 x:
a = append(a[:i], append([]T{x}, a[i:]...)...)
在索引 i 的位置插入長度為 j 的新切片:
a = append(a[:i], append(make([]T, j), a[i:]...)...)
在索引 i 的位置插入切片 b 的所有元素:
a = append(a[:i], append(b, a[i:]...)...)
取出位於切片 a 最末尾的元素 x:
x, a = a[len(a)-1], a[:len(a)-1]
將元素 x 追加到切片 a:
a = append(a, x)
因此,您可以使用切片和 append 操作來表示任意可變長度的序列。
從數學的角度來看,切片相當於向量,如果需要的話可以定義一個向量作為切片的別名來進行操作。
如果您需要更加完整的方案,可以學習一下 Eleanor McHugh 編寫的幾個包:slices、chain 和 lists。
7.6.8 切片和垃圾回收
切片的底層指向一個陣列,該陣列的實際容量可能要大於切片所定義的容量。只有在沒有任何切片指向的時候,底層的陣列記憶體才會被釋放,這種特性有時會導致程式佔用多餘的記憶體。
示例 函式 FindDigits
將一個檔案載入到記憶體,然後搜尋其中所有的數字並返回一個切片。
var digitRegexp = regexp.MustCompile("[0-9]+")
func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
return digitRegexp.Find(b)
}
這段程式碼可以順利執行,但返回的 []byte
指向的底層是整個檔案的資料。只要該返回的切片不被釋放,垃圾回收器就不能釋放整個檔案所佔用的記憶體。換句話說,一點點有用的資料卻佔用了整個檔案的記憶體。
想要避免這個問題,可以通過拷貝我們需要的部分到一個新的切片中:
func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = digitRegexp.Find(b)
c := make([]byte, len(b))
copy(c, b)
return c
}
事實上,上面這段程式碼只能找到第一個匹配正規表示式的數字串。要想找到所有的數字,可以嘗試下面這段程式碼:
func FindFileDigits(filename string) []byte {
fileBytes, _ := ioutil.ReadFile(filename)
b := digitRegexp.FindAll(fileBytes, len(fileBytes))
c := make([]byte, 0)
for _, bytes := range b {
c = append(c, bytes...)
}
return c
}
練習 7.12
編寫一個函式,要求其接受兩個引數,原始字串 str 和分割索引 i,然後返回兩個分割後的字串。
練習 7.13
假設有字串 str,那麼 str[len(str)/2:] + str[:len(str)/2]
的結果是什麼?
練習 7.14
編寫一個程式,要求能夠反轉字串,即將 “Google” 轉換成 “elgooG”(提示:使用 []byte
型別的切片)。
如果您使用兩個切片來實現反轉,請再嘗試使用一個切片(提示:使用交換法)。
如果您想要反轉 Unicode 編碼的字串,請使用 []int32
型別的切片。
練習 7.15
編寫一個程式,要求能夠遍歷一個陣列的字元,並將當前字元和前一個字元不相同的字元拷貝至另一個陣列。
練習 7.16
編寫一個程式,使用氣泡排序的方法排序一個包含整數的切片(演算法的定義可參考 維基百科)。
練習 7.17
在函數語言程式設計語言中,一個 map-function 是指能夠接受一個函式原型和一個列表,並使用列表中的值依次執行函式原型,公式為:map ( F(), (e1,e2, . . . ,en) ) = ( F(e1), F(e2), ... F(en) )
。
編寫一個函式 mapFunc
要求接受以下 2 個引數:
- 一個將整數乘以 10 的函式
- 一個整數列表
最後返回儲存執行結果的整數列表。
相關文章
- Go語言系列(三)之陣列和切片Go陣列
- go語言學習-陣列-切片-mapGo陣列
- Go 語言基礎 陣列、切片、對映Go陣列
- go語言之陣列與切片Go陣列
- Go中陣列和切片Go陣列
- Go 語言學習筆記之陣列與切片Go筆記陣列
- 聊聊Go語言中的陣列與切片Go陣列
- 介紹 Go 的陣列和切片Go陣列
- Go 陣列&切片Go陣列
- go 語言陣列Go陣列
- go陣列與切片Go陣列
- go(5)陣列,切片。Go陣列
- go 語言切片Go
- go語言教程之淺談陣列和切片的異同Go陣列
- Go語言————7.2 切片Go
- go中陣列,切片和append的玄學Go陣列APP
- 快速理解Go陣列和切片的內部實現原理Go陣列
- GO語言學習——切片二Go
- 小白對go陣列及切片的學習Go陣列
- Go 語言切片是如何擴容的?Go
- GO語言————8.4 map 型別的切片Go型別
- (二)golang陣列和切片Golang陣列
- Go語言————7.4 切片重組(reslice)Go
- golang切片和陣列的區別Golang陣列
- C語言指標(三):陣列指標和字串指標C語言指標陣列字串
- GO語言————7.5 切片的複製與追加Go
- GO語言————4.6 字串Go字串
- Go 語言在命令列以表格的形式輸出結構體切片Go命令列結構體
- Go 如何對陣列切片進行去重Go陣列
- 大資料開發-Go-陣列,切片大資料Go陣列
- VC++基礎 字串陣列的應用C++字串陣列
- c語言的陣列C語言陣列
- 陣列和字串陣列字串
- 《快學 Go 語言》第 5 課 —— 神奇的切片Go
- C語言陣列應用例項2: 掃雷遊戲C語言陣列遊戲
- 實驗4 c語言陣列應用程式設計C語言陣列程式設計
- Golang 陣列和切片 Slice 和 Map 使用Golang陣列
- 《快學 Go 語言》第 4 課 —— 低調的陣列Go陣列