Go语言中的字符串拼接方法介绍
本文介绍Go语言中的string类型、strings包和bytes.Buffer类型,介绍几种字符串拼接方法。
目录
- string类型
- strings包
- strings.Builder类型
- strings.Reader类型
- bytes.Buffer
- bytes.Buffer:写数据
- bytes.Buffer:读数据
- 字符串拼接
- 直接相加
- strings.Builder
- strings.Join()
- bytes.Buffer
- append方法
- fmt.Sprintf
- 字符串拼接性能测试
- 系列文章
string类型
string类型的值可以拆分为一个包含多个字符(rune类型)的序列,也可以被拆分为一个包含多个字节 (byte类型) 的序列。其中一个rune类型值代表一个Unicode 字符,一个rune类型值占用四个字节,底层就是一个 UTF-8 编码值,它其实是int32类型的一个别名类型。
package mainimport ("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。
strings包
strings.Builder类型
strings.Builder的优势主要体现在字符串拼接上,相比使用+
拼接,效率更高。
- strings.Builder已存在的值不可改变,只能重置(Reset()方法)或者拼接更多的内容。
- 一旦调用了Builder值,就不能再以任何方式对其进行复制,比如函数间值传递、通道传递值、把值赋予变量等。
- 在进行拼接时,Builder值会自动地对自身的内容容器进行扩容,也可以使用Grow方法进行手动扩容。
package mainimport ("fmt""strings"
)
func main() {var builder1 strings.Builderbuilder1.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
strings.Reader类型
strings.Reader类型可以用于高效地读取字符串,它通过使用已读计数机制来实现了高效读取,已读计数保存了已读取的字节数,也代表了下一次读取的起始索引位置。
package mainimport ("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 mainimport ("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 mainimport ("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!
bytes.Buffer
bytes包和strings包类似,strings包主要面向的是 Unicode 字符和经过 UTF-8 编码的字符串,而bytes包面对的则主要是字节和字节切片,主要作为字节序列的缓冲区。bytes.Buffer数据的读写都使用到了已读计数。
bytes.Buffer具有读和写功能,下面分别介绍他们的简单使用方法。
bytes.Buffer:写数据
和strings.Builder一样,bytes.Buffer可以用于拼接字符串,strings.Builder也会自动对内容容器进行扩容。请看下面的代码:
package mainimport ("bytes""fmt"
)func DemoBytes() {var buffer bytes.Bufferbuffer.WriteString("hello ")buffer.WriteString("world !")fmt.Println(buffer.String())
}
执行结果:
hello world !
bytes.Buffer:读数据
bytes.Buffer读数据也使用了已读计数,需要注意的是,进行读取操作后,Len方法返回的是未读内容的长度。下面直接来看代码:
package mainimport ("bytes""fmt"
)func DemoBytes() {var buffer bytes.Bufferbuffer.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())
}
执行结果:
helloworld !
The length of buffer: 8
字符串拼接
简单了解了string类型、strings包和bytes.Buffer类型后,下面来介绍golang中的字符串拼接方法。
https://zhuanlan.zhihu.com/p/349672248
go test -bench=. -run=^BenchmarkDemoBytes$
直接相加
最简单的方法是直接相加,由于string类型的值是不可变的,进行字符串拼接时会生成新的字符串,将拼接的字符串依次拷贝到一个新的连续内存空间中。如果存在大量字符串拼接操作,使用这种方法非常消耗内存。
package mainimport ("bytes""fmt""time"
)func main() {str1 := "hello "str2 := "world !"str3 := str1 + str2fmt.Println(str3)
}
strings.Builder
前面介绍了strings.Builder可以用于拼接字符串:
var builder1 strings.Builder
builder1.WriteString("hello ")
builder1.WriteString("world !")
strings.Join()
也可以使用strings.Join方法,其实Join()调用了WriteString方法;
str1 := "hello "
str2 := "world !"
str3 := ""str3 = strings.Join([]string{str3,str1},"")
str3 = strings.Join([]string{str3,str2},"")
bytes.Buffer
bytes.Buffer也可以用于拼接:
var buffer bytes.Bufferbuffer.WriteString("hello ")
buffer.WriteString("world !")
append方法
也可以使用Go内置函数append方法,用于拼接切片:
package mainimport ("fmt"
)func DemoAppend(n int) {str1 := "hello "str2 := "world !"var str3 []bytestr3 = append(str3, []byte(str1)...)str3 = append(str3, []byte(str2)...)fmt.Println(string(str3))
}
执行结果:
hello world !
fmt.Sprintf
fmt包中的Sprintf方法也可以用来拼接字符串:
str1 := "hello "
str2 := "world !"
str3 := fmt.Sprintf("%s%s", str1, str2)
字符串拼接性能测试
下面来测试一下这6种方法的性能,编写测试源码文件strcat_test.go:
package benchmarkimport ("bytes""fmt""strings""testing"
)func DemoBytesBuffer(n int) {var buffer bytes.Bufferfor i := 0; i < n; i++ {buffer.WriteString("hello ")buffer.WriteString("world !")}
}func DemoWriteString(n int) {var builder1 strings.Builderfor 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 += str1str3 += str2}
}func DemoAppend(n int) {str1 := "hello "str2 := "world !"var str3 []bytefor 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
--THE END--
系列文章
1. Go语言开发环境安装
2. Go语言基础语法(一)
3. Go语言基础语法(二):函数
4. Go语言基础语法(三):结构体及方法
5. Go语言中的字符串拼接方法介绍
6. Go语言中的通道
7. Go语言并发编程:原子操作
8. Go语言并发编程:互斥锁
9. Go语言并发编程:sync.Once
欢迎关注公众号:「测试开发小记」及时接收最新技术文章!
Go语言中的字符串拼接方法介绍相关推荐
- Go 语言中的字符串拼接
目录 1. 通过 + 号连接两个字符串 2. 使用 sprintf 函数 3. 使用 Join 函数 4. 使用 bytes.Buffer 的 WriteString 函数 5. 使用 buffer. ...
- go语言字符串换行_Go语言中的字符串处理方法示例详解
1 概述 字符串,string,一串固定长度的字符连接起来的字符集合.Go语言的字符串是使用UTF-8编码的.UTF-8是Unicode的实现方式之一. Go语言原生支持字符串.使用双引号(" ...
- c语言中的字符串拼接
在看内核源码时,看到这样一段代码: int __init ip_vs_protocol_init(void) {char protocols[64]; #define REGISTER_PROTOCO ...
- c语言mktime,在C语言中转换时间的基本方法介绍
C语言mktime()函数:将时间转换成经过的秒数头文件: #include 定义函数: time_t mktime(strcut tm * timeptr); 函数说明:mktime()用来将参数t ...
- atoi函数_每日干货丨C语言中的字符串处理库函数介绍与实现
strlen函数:求字符串的长度 size_t strlen(const char *s) 头文件:#include 说明:求出s指向的字符串的长度(不包括null字符). 返回值:返回s指向的字符串 ...
- C语言中怎么自动生成时间,在C语言中转换时间的基本方法介绍
C语言mktime()函数:将时间转换成经过的秒数头文件: #include 定义函数: time_t mktime(strcut tm * timeptr); 函数说明:mktime()用来将参数t ...
- c语言中用于获取字符串长度的函数是,C语言中求字符串长度的函数的几种实现方法...
C语言中求字符串长度的函数的几种实现方法 1.最常用的方法是创建一个计数器,判断是否遇到'\0',不是'\0'指针就往后加一. int my_strlen(const char *str) { ass ...
- python字符串截取方法_如何使用python语言中的字符串方法截取字符串
在我们使用python语言中的字符串方法时,可能会判断某个字符串是否以什么开头,可以使用什么进行截取等.下面利用几个实例说明字符串中的方法的用法,操作如下: 工具/原料 python 截图工具 方法/ ...
- 切割字符串长度php,C++_C语言中计算字符串长度与分割字符串的方法,C语言strlen()函数:返回字符串 - phpStudy...
C语言中计算字符串长度与分割字符串的方法 C语言strlen()函数:返回字符串的长度头文件: #include strlen()函数用来计算字符串的长度,其原型为: unsigned int str ...
最新文章
- yaml-cpp介绍
- 【归并排序】休息(jzoj 3462)
- MySQL - 行锁 表锁 乐观锁 悲观锁 读锁 写锁
- 超分辨率技术如何发展?这6篇ECCV 18论文带你一次尽览
- ACM 学习笔记(六) 图论
- 小程序反编译 g is not defined_阅读技巧 | 如何猜中作者的小心思?
- 手机APP测试如何进行兼容性测试?
- 限流算法:滑动时间窗口算法。
- html链接位置移动,锚点链接点击缓慢移动到目标位置
- 暴力解题之公务员行测资料分析技巧
- 2016计算机cpu,2016年12月电脑CPU天梯图一览
- Java设计模式的一些积累
- 众多交通工具3dm Rhino资源素材一键即可获取
- 主线程结束子线程会跟着结束吗
- 异步下载小说《诡秘之主》
- vba按原格式批量合并word文档
- 我把皮小浪の的 蓝色妖姬系列做进了java窗口
- 免费回收站恢复软件有哪些?数据恢复软件,这三款就足够了
- windows开启休眠
- STM32实现低功耗待机(电流低至5.7uA)