go语言string之Buffer与Builder

Python015

go语言string之Buffer与Builder,第1张

操作字符串离不开字符串的拼接,但是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类型返回了回来,去掉了申请空间的操作。

rune是Go语言中一种特殊的数据类型,它是int32的别名,几乎在所有方面等同于int32,用于区分字符值和整数值,官方解释如下:

下面我们通过一个例子来看一下:

我们猜测一下结果,hello5 个字符+1 个空格+3 个汉子,算起来应该是 9 个,长度为 9 才对,但是我们执行一下,

结果打印是 15,这是为什么呢?

所以计算出的长度就等于 5+1+3*3=15

如果我们需要计算出字符串的长度,而不是底层字节的个数,那么可以使用下面的方法:

运行结果如下:

在 rune 定义上方还有一个,byte = uint8

string字符串的长度可以用string中的成员函数length()或者size()来判断。两者功能相同,返回值都是字符串长度。

编写下列程序为例,通过length函数和size函数得到字符串“Test string”的长度,运行结果如下:

扩展资料:

C++ 中的 string 类的用法:

1、string类的构造函数:

string(const char *s)//用c字符串s初始化 string(int n,char c)//用n个字符c初始化

此外,string类还支持默认构造函数和复制构造函数,如string s1;string s2="hello";都是正确的写法。当构造的string太长而无法表达时会抛出length_error异常 。

2、string类的字符操作:

const char &operator[](int n)const const char &at(int n)const char &operator[](int n) char &at(int n);

operator[]和at()均返回当前字符串中第n个字符的位置,但at函数提供范围检查,当越界时会抛出out_of_range异常,下标运算符[]不提供检查访问。

const char *data()const//返回一个非null终止的c字符数组 const char *c_str()const//返回一个以null终止的c字符串

int copy(char *s, int n, int pos = 0) const//把当前串中以pos开始的n个字符拷贝到以s为起始位置的字符数组中,返回实际拷贝的数目