Go語言中的字符串拼接方法詳情
1、string類型
string
類型的值可以拆分為一個包含多個字符(rune類型)的序列,也可以被拆分為一個包含多個字節 (byte類型) 的序列。其中一個rune
類型值代表一個Unicode
字符,一個rune
類型值占用四個字節,底層就是一個 UTF-8 編碼值,它其實是int32類型的一個別名類型。
package main import ( "fmt" ) func main() { str := "你好world" fmt.Printf("The string: %q\n", str) fmt.Printf("runes(char): %q\n", []rune(str)) fmt.Printf("runes(hex): %x\n", []rune(str)) fmt.Printf("bytes(hex): [% x]\n", []byte(str)) }
執行結果:
The string: “你好world”
runes(char): [‘你’ ‘好’ ‘w’ ‘o’ ‘r’ ‘l’ ‘d’]
runes(hex): [4f60 597d 77 6f 72 6c 64]
bytes(hex): e4 bd a0 e5 a5 bd 77 6f 72 6c 64
可以看到,英文字符使用一個字節,而中文字符需要三個字節。下面使用 for range
語句對上面的字符串進行遍歷:
for index, value := range str { fmt.Printf("%d: %q [% x]\n", index, value, []byte(string(value))) }
執行結果如下:
0: ‘你’ [e4 bd a0]
3: ‘好’ [e5 a5 bd]
6: ‘w’ [77]
7: ‘o’ [6f]
8: ‘r’ [72]
9: ‘l’ [6c]
10: ‘d’ [64]
index
索引值不是0-6,相鄰Unicode
字符的索引值不一定是連續的,因為中文字符占用瞭3個字節,寬度為3。
2、strings包
2.1 strings.Builder類型
strings.Builder
的優勢主要體現在字符串拼接上,相比使用+拼接,效率更高。
strings.Builder
已存在的值不可改變,隻能重置(Reset()
方法)或者拼接更多的內容。- 一旦調用瞭
Builder
值,就不能再以任何方式對其進行復制,比如函數間值傳遞、通道傳遞值、把值賦予變量等。 - 在進行拼接時,
Builder
值會自動地對自身的內容容器進行擴容,也可以使用Grow
方法進行手動擴容。
package main import ( "fmt" "strings" ) func main() { var builder1 strings.Builder builder1.WriteString("hello") builder1.WriteByte(' ') builder1.WriteString("world") builder1.Write([]byte{' ', '!'}) fmt.Println(builder1.String()) f1 := func(b strings.Builder) { // b.WriteString("world !") //會報錯 } f1(builder1) builder1.Reset() fmt.Printf("The length 0f builder1: %d\n", builder1.Len()) }
執行結果:
hello world !
The length 0f builder1: 0
2.2 strings.Reader類型
strings.Reader
類型可以用於高效地讀取字符串,它通過使用已讀計數機制來實現瞭高效讀取,已讀計數保存瞭已讀取的字節數,也代表瞭下一次讀取的起始索引位置。
package main import ( "fmt" "strings" ) func main() { reader1 := strings.NewReader("hello world!") buf1 := make([]byte, 6) fmt.Printf("reading index: %d\n", reader1.Size()-int64(reader1.Len())) reader1.Read(buf1) fmt.Println(string(buf1)) fmt.Printf("reading index: %d\n", reader1.Size()-int64(reader1.Len())) reader1.Read(buf1) fmt.Println(string(buf1)) fmt.Printf("reading index: %d\n", reader1.Size()-int64(reader1.Len())) }
執行結果:
reading index: 0
hello
reading index: 6
world!
reading index: 12
可以看到,每讀取一次之後,已讀計數就會增加。
strings
包的ReadAt
方法不會依據已讀計數進行讀取,也不會更新已讀計數。它可以根據偏移量來自由地讀取Reader
值中的內容。
package main import ( "fmt" "strings" ) func main() { reader1 := strings.NewReader("hello world!") buf1 := make([]byte, 6) offset1 := int64(6) n, _ := reader1.ReadAt(buf1, offset1) fmt.Println(string(buf2)) }
執行結果:
world!
也可以使用Seek
方法來指定下一次讀取的起始索引位置。
package main import ( "fmt" "strings" "io" ) func main() { reader1 := strings.NewReader("hello world!") buf1 := make([]byte, 6) offset1 := int64(6) readingIndex, _ := reader2.Seek(offset1, io.SeekCurrent) fmt.Printf("reading index: %d\n", readingIndex) reader1.Read(buf1) fmt.Printf("reading index: %d\n", reader1.Size()-int64(reader1.Len())) fmt.Println(string(buf1)) }
執行結果:
reading index: 6
reading index: 12
world!
3、bytes.Buffer
bytes
包和strings
包類似,strings
包主要面向的是 Unicode
字符和經過 UTF-8 編碼的字符串,而bytes
包面對的則主要是字節和字節切片,主要作為字節序列的緩沖區。bytes.Buffer
數據的讀寫都使用到瞭已讀計數。
bytes.Buffer
具有讀和寫功能,下面分別介紹他們的簡單使用方法。
3.1 bytes.Buffer:寫數據
和strings.Builder
一樣,bytes.Buffer
可以用於拼接字符串,strings.Builder
也會自動對內容容器進行擴容。請看下面的代碼:
package main import ( "bytes" "fmt" ) func DemoBytes() { var buffer bytes.Buffer buffer.WriteString("hello ") buffer.WriteString("world !") fmt.Println(buffer.String()) }
執行結果:
hello world !
3.2 bytes.Buffer:讀數據
bytes.Buffer
讀數據也使用瞭已讀計數,需要註意的是,進行讀取操作後,Len
方法返回的是未讀內容的長度。下面直接來看代碼:
package main import ( "bytes" "fmt" ) func DemoBytes() { var buffer bytes.Buffer buffer.WriteString("hello ") buffer.WriteString("world !") p1 := make([]byte, 5) n, _ := buffer.Read(p1) fmt.Println(string(p1)) fmt.Println(buffer.String()) fmt.Printf("The length of buffer: %d\n", buffer.Len()) }
執行結果:
hello
world !
The length of buffer: 8
4、字符串拼接
簡單瞭解瞭string類型、strings包和bytes.Buffer類型後,下面來介紹golang中的字符串拼接方法。
https://zhuanlan.zhihu.com/p/349672248
go test -bench=. -run=^BenchmarkDemoBytes$
4.1 直接相加
最簡單的方法是直接相加,由於string類型的值是不可變的,進行字符串拼接時會生成新的字符串,將拼接的字符串依次拷貝到一個新的連續內存空間中。如果存在大量字符串拼接操作,使用這種方法非常消耗內存。
package main import ( "bytes" "fmt" "time" ) func main() { str1 := "hello " str2 := "world !" str3 := str1 + str2 fmt.Println(str3) }
4.2strings.Builder
前面介紹瞭strings.Builder
可以用於拼接字符串:
var builder1 strings.Builder builder1.WriteString("hello ") builder1.WriteString("world !")
4.3 strings.Join()
也可以使用strings.Join
方法,其實Join()
調用瞭WriteString
方法;
str1 := "hello " str2 := "world !" str3 := "" str3 = strings.Join([]string{str3,str1},"") str3 = strings.Join([]string{str3,str2},"")
4.4 bytes.Buffer
bytes.Buffer也可以用於拼接:
var buffer bytes.Buffer buffer.WriteString("hello ") buffer.WriteString("world !")
4.5 append方法
也可以使用Go
內置函數append
方法,用於拼接切片:
package main import ( "fmt" ) func DemoAppend(n int) { str1 := "hello " str2 := "world !" var str3 []byte str3 = append(str3, []byte(str1)...) str3 = append(str3, []byte(str2)...) fmt.Println(string(str3)) }
執行結果:
hello world !
4.6 fmt.Sprintf
fmt
包中的Sprintf
方法也可以用來拼接字符串:
str1 := "hello " str2 := "world !" str3 := fmt.Sprintf("%s%s", str1, str2)
5、字符串拼接性能測試
下面來測試一下這6種方法的性能,編寫測試源碼文件strcat_test.go
:
package benchmark import ( "bytes" "fmt" "strings" "testing" ) func DemoBytesBuffer(n int) { var buffer bytes.Buffer for i := 0; i < n; i++ { buffer.WriteString("hello ") buffer.WriteString("world !") } } func DemoWriteString(n int) { var builder1 strings.Builder for i := 0; i < n; i++ { builder1.WriteString("hello ") builder1.WriteString("world !") } } func DemoStringsJoin(n int) { str1 := "hello " str2 := "world !" str3 := "" for i := 0; i < n; i++ { str3 = strings.Join([]string{str3, str1}, "") str3 = strings.Join([]string{str3, str2}, "") } } func DemoPlus(n int) { str1 := "hello " str2 := "world !" str3 := "" for i := 0; i < n; i++ { str3 += str1 str3 += str2 } } func DemoAppend(n int) { str1 := "hello " str2 := "world !" var str3 []byte for i := 0; i < n; i++ { str3 = append(str3, []byte(str1)...) str3 = append(str3, []byte(str2)...) } } func DemoSprintf(n int) { str1 := "hello " str2 := "world !" str3 := "" for i := 0; i < n; i++ { str3 = fmt.Sprintf("%s%s", str3, str1) str3 = fmt.Sprintf("%s%s", str3, str2) } } func BenchmarkBytesBuffer(b *testing.B) { for i := 0; i < b.N; i++ { DemoBytesBuffer(10000) } } func BenchmarkWriteString(b *testing.B) { for i := 0; i < b.N; i++ { DemoWriteString(10000) } } func BenchmarkStringsJoin(b *testing.B) { for i := 0; i < b.N; i++ { DemoStringsJoin(10000) } } func BenchmarkAppend(b *testing.B) { for i := 0; i < b.N; i++ { DemoAppend(10000) } } func BenchmarkPlus(b *testing.B) { for i := 0; i < b.N; i++ { DemoPlus(10000) } } func BenchmarkSprintf(b *testing.B) { for i := 0; i < b.N; i++ { DemoSprintf(10000) } } 執行性能測試: $ go test -bench=. -run=^$ goos: windows goarch: amd64 pkg: testGo/benchmark cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz BenchmarkBytesBuffer-8 3436 326846 ns/op BenchmarkWriteString-8 4148 271453 ns/op BenchmarkStringsJoin-8 3 402266267 ns/op BenchmarkAppend-8 1923 618489 ns/op BenchmarkPlus-8 3 345087467 ns/op BenchmarkSprintf-8 2 628330850 ns/op PASS ok testGo/benchmark 9.279s
通過平均耗時可以看到WriteString
方法執行效率最高。Sprintf
方法效率最低。
- 我們看到
Strings.Join
方法效率也比較低,在上面的場景下它的效率比較低,它在合並已有字符串數組的場合效率是很高的。 - 如果要連續拼接大量字符串推薦使用
WriteString
方法,如果是少量字符串拼接,也可以直接使用+。 append
方法的效率也是很高的,它主要用於切片的拼接。fmt.Sprintf
方法雖然效率低,但在少量數據拼接中,如果你想拼接其它數據類型,使用它可以完美的解決:
name := "zhangsan" age := 20 str4 := fmt.Sprintf("%s is %d years old", name, age) fmt.Println(str4) // zhangsan is 20 years old
到此這篇關於Go語言中的字符串拼接方法詳情的文章就介紹到這瞭,更多相關Go語言中的字符串拼接方法內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- golang 字符串拼接性能的對比分析
- go語言中五種字符串的拼接方式(小結)
- Go語言如何高效的進行字符串拼接(6種方式對比分析)
- 詳解Golang中字符串的使用
- Golang語言如何高效拼接字符串詳解