golang中的字符串( 二 )

xo,可能我們會像下面這樣寫:
package mainimport ( "fmt" "strings")func main() { s := "xohelloxo" s = strings.TrimRight(s, "xo") fmt.Println(s)}go run 7.goxohell可以看到這不是我們期望的結果 。我們可以看下TrimRight的工作原理:

  1. 從右側取出第一個字符o,判斷是否在xo中,在就移除
  2. 重復步驟1,知道不符合條件
所以就可以解釋通了 。當然和它相似的TrimLeft和Trim也是一樣的原理 。
如果我們只想刪除最后xo可以使用TrimSuffix函數:
package mainimport ( "fmt" "strings")func main() { s := "xohelloxo" s = strings.TrimSuffix(s, "xo") fmt.Println(s)}go run 7.goxohello當然也有對應的從前面刪除的函數TrimPrefix 。
5、字符串連接開發中我們經常會用到連接字符串的操作,在go中我們一般有2種方式 。
我們先看下+號連接的方式:
package mainimport ( "fmt" "strings")func implode(values []string, operate string) string { s := "" for _, value := range values {s += operates += value } s = strings.TrimPrefix(s, operate) return s}func main() { a := []string{"hello", "world"} s := implode(a, " ") fmt.Println(s)}go run 7.go hello world這種方式的缺點就是,由于字符串的不變性,每次+號賦值的時候s不會被更新 , 而是重新分配內存,所以這種方式對性能有很大影響 。
還有一種方式就是使用strings.Builder:
package mainimport ( "fmt" "strings")func implode(values []string, operate string) string { sb := strings.Builder{} for _, value := range values {_, _ = sb.WriteString(operate)_, _ = sb.WriteString(value) } s := strings.TrimPrefix(sb.String(), operate) return s}func main() { a := []string{"hello", "world"} s := implode(a, " ") fmt.Println(s)}go run 7.gohello world首先 , 我們創建了一個 strings.Builder 結構 。在每次遍歷中 , 我們通過調用 WriteString 方法構造結果字符串,該方法將 value 的內容附加到其內部緩沖區 , 從而最大限度地減少內存復制 。
WriteString 的第二個參數返回的是error,但是error的值會一直為nil 。之所以有第二個error參數是因為我 strings.Builder 實現了 io.StringWriter 接口,它包含一個方法:WriteString(s string) (n int, err error) 。
我們看下WriteString的內部是什么樣的:
func (b *Builder) WriteString(s string) (int, error) { b.copyCheck() b.buf = append(b.buf, s...) return len(s), nil}我們可以看到b.buf是一個字節切片,而里面的實現是使用了append方法 。我們知道如果切片很大,使用append會讓底層數組不斷擴容,影響代碼執行效率 。
我們知道解決這個問題的方法是,如果事先知道切片的大?。?我們可以在初始化的時候就分配好切片的容量 。
所以上面的字符串連接還有一種優化方案:
package mainimport ( "fmt" "strings")func implode(values []string, operate string) string { total := 0 for i := 0; i < len(values); i++ {total += len(values[i]) } total += len(operate) * len(values) sb := strings.Builder{} sb.Grow(total) // 這里會重新分配b.buf的長度和容量 for _, value := range values {_, _ = sb.WriteString(operate)_, _ = sb.WriteString(value) } s := strings.TrimPrefix(sb.String(), operate) return s}func main() { a := []string{"hello", "world"} s := implode(a, " ") fmt.Println(s)}go run 7.gohello world6、字節切片轉字符串需要明確的是,字節切片轉換成字符串,需要復制一份副本出來 ??梢酝ㄟ^下面的代碼做驗證:
b := []byte{'a', 'b', 'c'}s := string(b)b[1] = 'x'fmt.Println(s)事實上,上面將會輸出abc而不是axc 。所以字節切片到字符串的轉換是有開銷的 。
但是我們開發中經常用到的包iio.Read之類的,入參或者返回經常是字節切片類型 。而我們調用這些函數時經常是以字符串的形式 , 導致我們不得不做一些字節切片刀字符串的轉換 。
所以結論是,當我們需要使用字符串作為入參或者返回時,我們首先要考慮的是能用字節切片的就用字節切片 。
【golang中的字符串】

推薦閱讀