天天看點

go語言學習----字元串、數組和切片的應用  字元串、數組和切片的應用

 字元串、數組和切片的應用

 從字元串生成位元組切片

假設 s 是一個字元串(本質上是一個位元組數組),那麼就可以直接通過 

c := []bytes(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 := []int(s)

 文法,這樣切片中的每個 int 都會包含對應的 Unicode 代碼,因為字元串中的每次字元都會對應一個整數。類似的,您也可以将字元串轉換為元素類型為 rune 的切片:

r := []rune(s)

可以通過代碼 

len([]int(s))

 來獲得字元串中字元的數量,但使用 

utf8.RuneCountInString(s)

 效率會更高一點。(參考count_characters.go)

您還可以将一個字元串追加到某一個字元數組的尾部:

var b []byte
var s string
b = append(b, s...)      

 擷取字元串的某一部分

使用 

substr := str[start:end]

 可以從字元串 str 擷取到從索引 start 開始到 

end-1

 位置的子字元串。同樣的,

str[start:]

 則表示擷取從 start 開始到 

len(str)-1

 位置的子字元串。而 

str[:end]

 表示擷取從 0 開始到 

end-1

的子字元串。

字元串和切片的記憶體結構

在記憶體中,一個字元串實際上是一個雙字結構,即一個指向實際資料的指針和記錄字元串長度的整數(見圖 7.4)。因為指針對使用者來說是完全不可見,是以我們可以依舊把字元串看做是一個值類型,也就是一個字元數組。

字元串 

string s = "hello"

 和子字元串 

t = s[2:3]

 在記憶體中的結構可以用下圖表示:

go語言學習----字元串、數組和切片的應用  字元串、數組和切片的應用

 修改字元串中的某個字元

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"      

是以,您可以通過操作切片來完成對字元串的操作。

位元組數組對比函數

下面的 

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 // 數組相等
}      

搜尋及排序切片和數組

标準庫提供了 

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 節對它的細節進行深入,并實作一個屬于我們自己的版本。

append 函數常見操作

我們在第 7.5 節提到的 append 非常有用,它能夠用于各種方面的操作:

  1. 将切片 b 的元素追加到切片 a 之後:

    a = append(a, b...)

  2. 複制切片 a 的元素到新的切片 b 上:
    b = make([]T, len(a))
    copy(b, a)      
  3. 删除位于索引 i 的元素:

    a = append(a[:i], a[i+1:]...)

  4. 切除切片 a 中從索引 i 至 j 位置的元素:

    a = append(a[:i], a[j:]...)

  5. 為切片 a 擴充 j 個元素長度:

    a = append(a, make([]T, j)...)

  6. 在索引 i 的位置插入元素 x:

    a = append(a[:i], append([]T{x}, a[i:]...)...)

  7. 在索引 i 的位置插入長度為 j 的新切片:

    a = append(a[:i], append(make([]T, j), a[i:]...)...)

  8. 在索引 i 的位置插入切片 b 的所有元素:

    a = append(a[:i], append(b, a[i:]...)...)

  9. 取出位于切片 a 最末尾的元素 x:

    x, a = a[len(a)-1], a[:len(a)-1]

  10. 将元素 x 追加到切片 a:

    a = append(a, x)

是以,您可以使用切片和 append 操作來表示任意可變長度的序列。

從數學的角度來看,切片相當于向量,如果需要的話可以定義一個向量作為切片的别名來進行操作。

如果您需要更加完整的方案,可以學習一下 Eleanor McHugh 編寫的幾個包:slices、chain 和 lists。

切片和垃圾回收

切片的底層指向一個數組,該數組的實際體積可能要大于切片所定義的體積。隻有在沒有任何切片指向的時候,底層的數組内層才會被釋放,這種特性有時會導緻程式占用多餘的記憶體。

示例 函數 

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
}