Go語言中的字符串拼接方法詳情
1、string類型
string類型的值可以拆分為一個包含多個字符(rune類型)的序列,也可以被拆分為一個包含多個字節(jié) (byte類型) 的序列。其中一個rune類型值代表一個Unicode 字符,一個rune類型值占用四個字節(jié),底層就是一個 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))
}
執(zhí)行結(jié)果:
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
可以看到,英文字符使用一個字節(jié),而中文字符需要三個字節(jié)。下面使用 for range 語句對上面的字符串進行遍歷:
for index, value := range str {
fmt.Printf("%d: %q [% x]\n", index, value, []byte(string(value)))
}
執(zhí)行結(jié)果如下:
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 字符的索引值不一定是連續(xù)的,因為中文字符占用了3個字節(jié),寬度為3。
2、strings包
2.1 strings.Builder類型
strings.Builder的優(yōu)勢主要體現(xiàn)在字符串拼接上,相比使用+拼接,效率更高。
strings.Builder已存在的值不可改變,只能重置(Reset()方法)或者拼接更多的內(nèi)容。- 一旦調(diào)用了
Builder值,就不能再以任何方式對其進行復(fù)制,比如函數(shù)間值傳遞、通道傳遞值、把值賦予變量等。 - 在進行拼接時,
Builder值會自動地對自身的內(nèi)容容器進行擴容,也可以使用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())
}
執(zhí)行結(jié)果:
hello world !
The length 0f builder1: 0
2.2 strings.Reader類型
strings.Reader類型可以用于高效地讀取字符串,它通過使用已讀計數(shù)機制來實現(xiàn)了高效讀取,已讀計數(shù)保存了已讀取的字節(jié)數(shù),也代表了下一次讀取的起始索引位置。
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()))
}
執(zhí)行結(jié)果:
reading index: 0
hello
reading index: 6
world!
reading index: 12
可以看到,每讀取一次之后,已讀計數(shù)就會增加。
strings包的ReadAt方法不會依據(jù)已讀計數(shù)進行讀取,也不會更新已讀計數(shù)。它可以根據(jù)偏移量來自由地讀取Reader值中的內(nèi)容。
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))
}
執(zhí)行結(jié)果:
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))
}
執(zhí)行結(jié)果:
reading index: 6
reading index: 12
world!
3、bytes.Buffer
bytes包和strings包類似,strings包主要面向的是 Unicode 字符和經(jīng)過 UTF-8 編碼的字符串,而bytes包面對的則主要是字節(jié)和字節(jié)切片,主要作為字節(jié)序列的緩沖區(qū)。bytes.Buffer數(shù)據(jù)的讀寫都使用到了已讀計數(shù)。
bytes.Buffer具有讀和寫功能,下面分別介紹他們的簡單使用方法。
3.1 bytes.Buffer:寫數(shù)據(jù)
和strings.Builder一樣,bytes.Buffer可以用于拼接字符串,strings.Builder也會自動對內(nèi)容容器進行擴容。請看下面的代碼:
package main
import (
"bytes"
"fmt"
)
func DemoBytes() {
var buffer bytes.Buffer
buffer.WriteString("hello ")
buffer.WriteString("world !")
fmt.Println(buffer.String())
}
執(zhí)行結(jié)果:
hello world !
3.2 bytes.Buffer:讀數(shù)據(jù)
bytes.Buffer讀數(shù)據(jù)也使用了已讀計數(shù),需要注意的是,進行讀取操作后,Len方法返回的是未讀內(nèi)容的長度。下面直接來看代碼:
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())
}
執(zhí)行結(jié)果:
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類型的值是不可變的,進行字符串拼接時會生成新的字符串,將拼接的字符串依次拷貝到一個新的連續(xù)內(nèi)存空間中。如果存在大量字符串拼接操作,使用這種方法非常消耗內(nèi)存。
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()調(diào)用了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內(nèi)置函數(shù)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))
}
執(zhí)行結(jié)果:
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)
}
}
執(zhí)行性能測試:
$ 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方法執(zhí)行效率最高。Sprintf方法效率最低。
- 我們看到
Strings.Join方法效率也比較低,在上面的場景下它的效率比較低,它在合并已有字符串?dāng)?shù)組的場合效率是很高的。 - 如果要連續(xù)拼接大量字符串推薦使用
WriteString方法,如果是少量字符串拼接,也可以直接使用+。 append方法的效率也是很高的,它主要用于切片的拼接。fmt.Sprintf方法雖然效率低,但在少量數(shù)據(jù)拼接中,如果你想拼接其它數(shù)據(jù)類型,使用它可以完美的解決:
name := "zhangsan"
age := 20
str4 := fmt.Sprintf("%s is %d years old", name, age)
fmt.Println(str4) // zhangsan is 20 years old
到此這篇關(guān)于Go語言中的字符串拼接方法詳情的文章就介紹到這了,更多相關(guān)Go語言中的字符串拼接方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang調(diào)試bug及性能監(jiān)控方式實踐總結(jié)
這篇文章主要為大家介紹了golang調(diào)試bug及性能監(jiān)控方式實踐是總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05
Go1.16新特性embed打包靜態(tài)資源文件實現(xiàn)
這篇文章主要為大家介紹了Go?1.16新特性embed打包靜態(tài)資源文件的實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07
Go語言學(xué)習(xí)之?dāng)?shù)組的用法詳解
數(shù)組是相同數(shù)據(jù)類型的一組數(shù)據(jù)的集合,數(shù)組一旦定義長度不能修改,數(shù)組可以通過下標(biāo)(或者叫索引)來訪問元素。本文將通過示例詳細講解Go語言中數(shù)組的使用,需要的可以參考一下2022-04-04

