Golang :字符串连接的不同方法 利用字节缓冲区 bytes.Buffer 创建字符串

bytes.Buffer 是 Golang 标准库中的缓冲区,具有读写方法和可变大小的字节存储功能。缓冲区的零值是一个待使用的空缓冲区。定义如下:

type Buffer struct {
	buf      []byte // contents are the bytes buf[off : len(buf)]
	off      int    // read at &buf[off], write at &buf[len(buf)]
	lastRead readOp // last read operation, so that Unread* can work correctly.
}
注意要点:
(1)从 bytes.Buffer 读取数据后,被成功读取的数据仍保留在原缓冲区,只是无法被使用,因为缓冲区的可见数据从偏移 off 开始,即buf[off : len(buf)]

(1)声明一个 Buffer。

var b bytes.Buffer       				//直接定义一个Buffer变量,不用初始化,可以直接使用
b := new(bytes.Buffer)   				//使用New返回Buffer变量
b := bytes.NewBuffer(s []byte)   		//从一个[]byte切片,构造一个Buffer
b := bytes.NewBufferString(s string)	//从一个string变量,构造一个Buffer

(2)往 Buffer 中写入数据。

b.Write(d []byte) (n int, err error)   			//将切片d写入Buffer尾部
b.WriteString(s string) (n int, err error) 		//将字符串s写入Buffer尾部
b.WriteByte(c byte) error  						//将字符c写入Buffer尾部
b.WriteRune(r rune) (n int, err error)    		//将一个rune类型的数据放到缓冲区的尾部
b.ReadFrom(r io.Reader) (n int64, err error)	//从实现了io.Reader接口的可读取对象写入Buffer尾部

(3)从 Buffer 中读取数据。

//读取 n 个字节数据并返回,如果 buffer 不足 n 字节,则读取全部
b.Next(n int) []byte

//一次读取 len(p) 个 byte 到 p 中,每次读取新的内容将覆盖p中原来的内容。成功返回实际读取的字节数,off 向后偏移 n,buffer 没有数据返回错误 io.EOF
b.Read(p []byte) (n int, err error)

//读取第一个byte并返回,off 向后偏移 n
b.ReadByte() (byte, error)

//读取第一个 UTF8 编码的字符并返回该字符和该字符的字节数,b的第1个rune被拿掉。如果buffer为空,返回错误 io.EOF,如果不是UTF8编码的字符,则消费一个字节,返回 (U+FFFD,1,nil)
b.ReadRune() (r rune, size int, err error)

//读取缓冲区第一个分隔符前面的内容以及分隔符并返回,缓冲区会清空读取的内容。如果没有发现分隔符,则返回读取的内容并返回错误io.EOF
b.ReadBytes(delimiter byte) (line []byte, err error)

//读取缓冲区第一个分隔符前面的内容以及分隔符并作为字符串返回,缓冲区会清空读取的内容。如果没有发现分隔符,则返回读取的内容并返回错误 io.EOF
b.ReadString(delimiter byte) (line string, err error)

//将 Buffer 中的内容输出到实现了 io.Writer 接口的可写入对象中,成功返回写入的字节数,失败返回错误
b.WriteTo(w io.Writer) (n int64, err error)

(4)其它操作。

b.Bytes() []byte		//返回字节切片
b.Cap() int				//返回 buffer 内部字节切片的容量
b.Grow(n int)			//为 buffer 内部字节切片的容量增加 n 字节
b.Len() int				//返回缓冲区数据长度,等于 len(b.Bytes())
b.Reset() 				//清空数据
b.String() string		//字符串化
b.Truncate(n int)		//丢弃缓冲区中除前n个未读字节以外的所有字节。如果 n 为负数或大于缓冲区长度,则引发 panic
b.UnreadByte() error	//将最后一次读取操作中被成功读取的字节设为未被读取的状态,即将已读取的偏移 off 减 1
b.UnreadRune() error	//将最后一次 ReadRune() 读取操作返回的 UTF8 字符 rune设为未被读取的状态,即将已读取的偏移 off 减去 字符 rune 的字节数

Demo:
https://blog.csdn.net/liuxinmingcode/article/details/50405172 ( 不同方法的例子)
从文件 test.txt 中读取全部内容追加到 buffer 尾部。
test.txt 的内容为:

aaa

具体实现:

package main

import (
	"os"
	"fmt"
	"bytes"
)

func main() {
    file,_ := os.Open("/Users/xxx/go/src/awesomeProject1/buffer/test.txt")
    buf := bytes.NewBufferString("Hello world ")    
    buf.ReadFrom(file)              //将text.txt内容追加到缓冲器的尾部    
    fmt.Println(buf.String())
}

结果:

Hello world aaa

bytes buffer:就是写byte或者字符串string的一个容器
利用字节缓冲区 bytes.Buffer 创建字符串

package main

import (
	"bytes"
	"fmt"
)

func main() {
	var buffer bytes.Buffer
	buffer.WriteString("www.baidu.com")
	buffer.WriteString(":")
	buffer.WriteString("welcome!")
	buffer.WriteString("\n")
	buffer.WriteString("over...")

	fmt.Println("buffer: ",buffer)
	fmt.Printf("%T\n",buffer)

	fmt.Println("the string of buffer: ",buffer.String())
	fmt.Printf("%T\n",buffer.String())

输出结果:
在这里插入图片描述

golang 字符串的连接方式 :

  1. 直接使用运算符
func BenchmarkAddStringWithOperator(b *testing.B) {
	hello := "hello"
	world := "world"
	for i := 0; i < b.N; i++ {
		_ = hello + "," + world
	}
}

golang 里面的字符串都是不可变的,每次运算都会产生一个新的字符串,所以会产生很多临时的无用的字符串,不仅没有用,还会给 gc 带来额外的负担,所以性能比较差

  1. fmt.Sprintf()
func BenchmarkAddStringWithSprintf(b *testing.B) {
	hello := "hello"
	world := "world"
	for i := 0; i < b.N; i++ {
		_ = fmt.Sprintf("%s,%s", hello, world)
	}
}

内部使用 []byte 实现,不像直接运算符这种会产生很多临时的字符串,但是内部的逻辑比较复杂,有很多额外的判断,还用到了 interface,所以性能也不是很好

  1. strings.Join()
func BenchmarkAddStringWithJoin(b *testing.B) {
	hello := "hello"
	world := "world"
	for i := 0; i < b.N; i++ {
		_ = strings.Join([]string{hello, world}, ",")
	}
}

join会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入,在已有一个数组的情况下,这种效率会很高,但是本来没有,去构造这个数据的代价也不小

  1. buffer.WriteString()
func BenchmarkAddStringWithBuffer(b *testing.B) {
	hello := "hello"
	world := "world"
	for i := 0; i < 1000; i++ {
		var buffer bytes.Buffer
		buffer.WriteString(hello)
		buffer.WriteString(",")
		buffer.WriteString(world)
		_ = buffer.String()
	}
}

这个比较理想,可以当成可变字符使用,对内存的增长也有优化,如果能预估字符串的长度,还可以用 buffer.Grow() 接口来设置 capacity

结论

1.在已有字符串数组的场合,使用 strings.Join() 能有比较好的性能
2.在一些性能要求较高的场合,尽量使用 buffer.WriteString() 以获得更好的性能
3.性能要求不太高的场合,直接使用运算符,代码更简短清晰,能获得比较好的可读性
4.如果需要拼接的不仅仅是字符串,还有数字之类的其他需求的话,可以考虑 fmt.Sprintf
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值