博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Golang理解-字符串拼接的几种方式
阅读量:4929 次
发布时间:2019-06-11

本文共 3078 字,大约阅读时间需要 10 分钟。

Golang中的字符串

Golang 中的string类型存储的字符串是不可变的, 如果要修改string内容需要将string转换为[]byte或[]rune,并且修改后的string内容是重新分配的, 原字符串将被gc回收;

package mainimport (        "fmt")func main() {    s := "hi, go"    fmt.Printf("value of str: %v\n", s)    fmt.Printf("ptr of str: %p\n", &s)    // 修改, 将,修改为!    bs := []byte(str)    bs[2] = '!'     fmt.Printf("value of lstr: %v\n", string(bs))    fmt.Printf("ptr of lstr: %p\n", &bs)}

结果:

value of str: hi, go

ptr of str: 0xc00000e1f0
value of lstr: hi! go
ptr of lstr: 0xc00000a080

可以看到bs 和 s 的地址空间不同了,可见字符串的修改是会重新分配的;

Golang中string有2种类型, 只包含ASCII码的string, 已经包含中文等其他复杂类型的string; 我们知道中文是占3个字节的;

其中:只包含ASCII码的string的string 能通过索引的方式查找对应位置的字符;而包含中文的string类型rune,要想完整的显示中文,需要使用for…range循环;

Golang字符串拼接方法

golang中要实现字符串的拼接,有很多种方法,最常见的当然是使用运算符"+"进行拼接了,还有很多其他的方法,下面依次介绍,并说明其优缺点.

直接使用运算符

// 不换行str := "hello, " + "golang"// 换行,换行是"+" 必须在上一行的结尾处str1 := "The only person " +                "standing in your way " +                "is you"

上面提到golang里面的字符串都是不可变的,每次运算都会产生一个新的字符串.

所以使用运算符"+"连接字符串会产生很多临时的无用的字符串,会给 gc 带来额外的负担,所以性能比较差.


使用fmt.Sprintf

fmt包是golang中的基础包,fmt 包实现了格式化I/O函数,类似于C的 printf 和 scanf.

在fmt包中提供了一个方法func Sprintf(format string, a ...interface{}) string,使用格式话的方式可以将多个字符串拼接到一起

str := fmt.Sprintf("%s %s %s", "format", "string", "by fmt.Sprintf")

这种方式,使用简单,虽然不会像"+"连接那样生成多余的string,但是内部实现颇为复杂,性能不是很好.

使用strings.Join

golang的 strings 包为字符串的拼接提供了一个方法func Join(a []string, sep string) string, Join的内部实现比fmt.Sprintf要简单的多,思路就是: Join会先根据字符串数组的内容,计算出一个拼接之后的长度,然后申请对应大小的内存,一个一个字符串填入.代码如下:

// Join 将传如的字符串连接成一个字符串func Join(a []string, sep string) string {    // 如果字符串数量少,直接使用运算符拼接    switch len(a) {    case 0:      return ""    case 1:      return a[0]     case 2:      return a[0] + sep + a[1]    case 3:      return a[0] + sep + a[1] + sep + a[2]    }    // 计算最终字符串的字符大小    // 首先计算连接符sep的大小    n := len(sep) * (len(a) -1)    // 计算被连接的字符串的字符数    for _, value := range a {      n += len(value)    }    // 知道了总的字符数量,创建对应大小的数组    b := make([]byte, n)    bp := copy(b, a[0])    for _, s := range a[1:] {      bp += copy(b[bp:], sep)      bp += copy(b[bp:], s)    }    return string(b)}

这种方式实现字符串的拼接,简单方便,效率也是很高的,建议使用,唯一的不足就是在生成数组的时候开销比较大;

使用bytes.Buffer

bytes包中的Buffer提供了一个方法 func (b *Buffer) WriteString(s string) (n int, err error)

WriteString将s的内容追加到缓冲区,并根据需要增加缓冲区。返回值n为s的长度;err总是nil。

如果缓冲区太大,WriteString将会因为ErrTooLarge而陷入恐慌。

package mainimport (    "fmt"    "bytes")func main() {    // 声明一个Buffer    var buf bytes.Buffer    buf.WriteString("good ")    buf.WriteString("boy!")    fmt.Println(buf.String()) // good boy!}

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

使用strings.Builder

strings.Builder 内部通过 slice 来保存和管理内容。slice 内部则是通过一个指针指向实际保存内容的数组。

strings.Builder 同样也提供了 Grow() 来支持预定义容量。

当我们可以预定义我们需要使用的容量时,strings.Builder 就能避免扩容而创建新的 slice 了。strings.Builder是非线程安全,性能上和 bytes.Buffer 相差无几。

package mainimport (    "fmt"    "strings")func main() {    // 声明一个Buffer    var buf strings.Builder    buf.WriteString("good ")    buf.WriteString("boy!")    fmt.Println(buf.String()) // good boy!}

1265401-20190807202533899-1471279105.png

转载于:https://www.cnblogs.com/vinsent/p/11281777.html

你可能感兴趣的文章
重构父类方法和返回值
查看>>
【原创】字符串工具类--驼峰法与下划线法互转
查看>>
模块化开发规范
查看>>
POJ 2642 The Brick Stops Here 0-1背包
查看>>
格式化操作
查看>>
DDX和DDV——控件与变量之间值的传递
查看>>
bzoj1093: [ZJOI2007]最大半连通子图
查看>>
javascript 数组以及对象的深拷贝(复制数组或复制对象)的方法
查看>>
html & angularJS-一个简单的10s倒计时
查看>>
【转载】六种位运算符
查看>>
DDR3详解(以Micron MT41J128M8 1Gb DDR3 SDRAM为例) 一
查看>>
CACHE的Miss和Hit
查看>>
linux shell 管道命令(pipe)使用及与shell重定向区别
查看>>
container_of宏
查看>>
模板类
查看>>
数据分析之pandas( 三 )
查看>>
Nosql(以redis、memchache以及mogoDB为代表)和关系型数据库的区别
查看>>
javascript 书写规范代码
查看>>
if __name__ == "__main__"的疑惑
查看>>
PHP带参数传值调用python脚本
查看>>