bufferredwrite(go语言string之Buffer与Builder)
本文目录
go语言string之Buffer与Builder
操作字符串离不开字符串的拼接,但是Go中string是只读类型,大量字符串的拼接会造成性能问题。
拼接字符串,无外乎四种方式,采用“+”,“fmt.Sprintf()”,"bytes.Buffer","strings.Builder"
上面我们创建10万字符串拼接的测试,可以发现"bytes.Buffer","strings.Builder"的性能最好,约是“+”的1000倍级别。
这是由于string是不可修改的,所以在使用“+”进行拼接字符串,每次都会产生申请空间,拼接,复制等操作,数据量大的情况下非常消耗资源和性能。而采用Buffer等方式,都是预先计算拼接字符串数组的总长度(如果可以知道长度),申请空间,底层是slice数组,可以以append的形式向后进行追加。最后在转换为字符串。这申请了不断申请空间的操作,也减少了空间的使用和拷贝的次数,自然性能也高不少。
bytes.buffer是一个缓冲byte类型的缓冲器存放着都是byte
是一个变长的 buffer,具有 Read 和Write 方法。 Buffer 的 零值 是一个 空的 buffer,但是可以使用,底层就是一个 byte, 字节切片。
向Buffer中写数据,可以看出Buffer中有个Grow函数用于对切片进行扩容。
从Buffer中读取数据
strings.Builder的方法和bytes.Buffer的方法的命名几乎一致。
但实现并不一致,Builder的Write方法直接将字符拼接slice数组后。
其没有提供read方法,但提供了strings.Reader方式
Reader 结构:
Buffer:
Builder:
可以看出Buffer和Builder底层都是采用byte数组进行装载数据。
先来说说Buffer:
创建好Buffer是一个empty的,off 用于指向读写的尾部。
在写的时候,先判断当前写入字符串长度是否大于Buffer的容量,如果大于就调用grow进行扩容,扩容申请的长度为当前写入字符串的长度。如果当前写入字符串长度小于最小字节长度64,直接创建64长度的byte数组。如果申请的长度小于二分之一总容量减去当前字符总长度,说明存在很大一部分被使用但已读,可以将未读的数据滑动到数组头。如果容量不足,扩展2*c + n 。
其String()方法就是将字节数组强转为string
Builder是如何实现的。
Builder采用append的方式向字节数组后添加字符串。
从上面可以看出,byte的内存大小也是以倍数进行申请的,初始大小为 0,第一次为大于当前申请的最大 2 的指数,不够进行翻倍.
可以看出如果旧容量小于1024进行翻倍,否则扩展四分之一。(2048 byte 后,申请策略的调整)。
其次String()方法与Buffer的string方法也有明显区别。Buffer的string是一种强转,我们知道在强转的时候是需要进行申请空间,并拷贝的。而Builder只是指针的转换。
这里我们解析一下 *(*string)(unsafe.Pointer(&b.buf)) 这个语句的意思。
先来了解下unsafe.Pointer 的用法。
也就是说,unsafe.Pointer 可以转换为任意类型,那么意味着,通过unsafe.Pointer媒介,程序绕过类型系统,进行地址转换而不是拷贝。
即*A =》 Pointer =》 *B
就像上面例子一样,将字节数组转为unsafe.Pointer类型,再转为string类型,s和b中内容一样,修改b,s也变了,说明b和s是同一个地址。但是对s重新赋值后,意味着s的地址指向了“WORLD”,它们所使用的内存空间不同了,所以s改变后,b并不会改变。
所以他们的区别就在于 bytes.Buffer 是重新申请了一块空间,存放生成的string变量, 而strings.Builder直接将底层的byte转换成了string类型返回了回来,去掉了申请空间的操作。
浅析nodejs的buffer比较怎么比较的
Buffer结构
Buffer是一个典型的Javascript和C++结合的模块,性能相关部分用C++实现,非性能相关部分用javascript实现。
Node在进程启动时Buffer就已经加装进入内存,并将其放入全局对象,因此无需require
Buffer对象:类似于数组,其元素是16进制的两位数。
Buffer内存分配
Buffer对象的内存分配不是在V8的堆内存中,在Node的C++层面实现内存的申请。
为了高效的使用申请来得内存,Node中采用slab分配机制,slab是一种动态内存管理机制,应用各种*nix操作系统。slab有三种状态:
(1) full:完全分配状态
(2) partial:部分分配状态
(3) empty:没有被分配状态
Buffer的转换
Buffer对象可以和字符串相互转换,支持的编码类型如下:
ASCII、UTF-8、UTF-16LE/UCS-2、Base64、Binary、Hex
字符串转Buffer
new Buffer(str, ),默认UTF-8
buf.write(string, )
Buffer转字符串
buf.toString()
Buffer不支持的编码类型
通过Buffer.isEncoding(encoding)判断是否支持
iconv-lite:纯JavaScript实现,更轻量,性能更好无需C++到javascript的转换
iconv:调用C++的libiconv库完成
Buffer的拼接
注意 "res.on(’data’, function(chunk) {})",其中的参数chunk是Buffer对象,直接用+拼接会自动转换为字符串,对于宽字节字符可能会导致乱码产生
解决方法:
(1) 通过可读流中的setEncoding()方法,该方法可以让data事件传递不再是Buffer对象,而是编码后的字符串,其内部使用了StringEncoder模块。
(2) 将Buffer对象暂存到数组中,最后在组装成一个大Buffer让后编码转换为字符串输出。
Buffer在文件I/O和网络I/O中广泛应用,其性能举足轻重,比普通字符串性能要高出很多。
Buffer的使用除了与字符串的转换有性能损耗外,在文件读取时候,有一个highWaterMark设置对性能影响至关重要。
a,highWaterMark设置对Buffer内存的分配和使用有一定影响。
b, highWaterMark设置过小,可能导致系统调用次数过多。
什么时候该用buffer,什么时候不该用 ------ 纯粹的javascript支持unicode码而对二进制不是很支持,当解决TCP流或者文件流的时候,处理流是有必要的,我们保存非utf-8字符串,2进制等等其他格式的时候,我们就必须得使用 ”Buffer“ 。
is.read(buffer) 是什么意思
is应该是InputStream的缩写把,回去好好看看api read(bu f fer)中buffer是一个缓冲区 把数据先读到内存中,然后write(bu f fer,0,400)写道OutputStream中,每次都是从buffer内存处的0偏移开始写,每次写400字节。
更多文章:
java数字字符串(java语言中如何判断字符串是否为数字)
2026年4月20日 18:20
java多线程和cpu的线程区别(在java中单线程和多线程是什么意思,他们有什么区别,分别的作用是什么)
2026年4月20日 18:00
js字符串转化为数字类型(js 里面怎么把字符串转换成数字)
2026年4月20日 17:40
特斯拉model y降价(特斯拉又降价新款Model Y曝光 全新包围+旋转中控)
2026年4月20日 17:20
梵谷 莫内 这是两个人是么他们是画画的么他们都有什么故事,不要太长,言简意赅就好?莫内莫奈
2026年4月20日 17:00
bufferredwrite(go语言string之Buffer与Builder)
2026年4月20日 16:40
律师咨询免费24小时在线婚姻官司(离婚免费律师咨询在线24小时)
2026年4月20日 16:20
git bash命令(git bash怎么用cd命令切换路径写法)
2026年4月20日 15:40




