• 高效操作Golang字符串实战教程


    Go可以对字符串直接连接,但更有效的方法是是使用Builder结构,和java中StringBuffer类似。本文介绍如何通过strings.Builder高效构建字符串,通过示例介绍WriteString 和 WriteRune方法,并进行性能对比builder的优势。最后列出常用的字符串操作方法。

    Builder介绍

    Builder是Go内置结构体,源码如下:

    // A Builder is used to efficiently build a string using Write methods.
    // It minimizes memory copying. The zero value is ready to use.
    // Do not copy a non-zero Builder.
    type Builder struct {
    	addr *Builder // of receiver, to detect copies by value
    	buf  []byte
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其内部为字节数组,动态分配内存,但不是每次都分配:

    // grow copies the buffer to a new, larger buffer so that there are at least n
    // bytes of capacity beyond len(b.buf).
    func (b *Builder) grow(n int) {
    	buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
    	copy(buf, b.buf)
    	b.buf = buf
    }
    
    // Grow grows b's capacity, if necessary, to guarantee space for
    // another n bytes. After Grow(n), at least n bytes can be written to b
    // without another allocation. If n is negative, Grow panics.
    func (b *Builder) Grow(n int) {
    	b.copyCheck()
    	if n < 0 {
    		panic("strings.Builder.Grow: negative count")
    	}
    	if cap(b.buf)-len(b.buf) < n {
    		b.grow(n)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    WriteString方法

    下面示例使用strings.Builder构建字符串:

    package main
    
    import (
        "fmt"
        "strings"
    )
    
    func main() {
    
        builder := strings.Builder{}
    
        builder.WriteString("There")
        builder.WriteString(" are")
        builder.WriteString(" three")
        builder.WriteString(" hawks")
        builder.WriteString(" in the sky")
    
        fmt.Println(builder.String())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    使用WriteString方法构建字符串,输出结果为:

    go run simple.go 
    There are three hawks in the sky
    
    • 1
    • 2

    Write方法

    下面示例通过字节slice构建字符串:

    package main
    
    import (
        "fmt"
        "strings"
    )
    
    func main() {
    
        builder := strings.Builder{}
    
        data1 := []byte{72, 101, 108, 108, 111}
        data2 := []byte{32}
        data3 := []byte{116, 104, 101, 114, 101, 33}
    
        builder.Write(data1)
        builder.Write(data2)
        builder.Write(data3)
    
        fmt.Println(builder.String())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    上面示例通过Write方法构建字符串,输出结果为:

    go run simple2.go 
    Hello there!
    
    • 1
    • 2

    另外还有 builder.WriteByte(),builder.WriteRune()方法,每次写一个字节。

    性能对比

    下面比较Builder与字符串连接的性能:

    package main
    
    import (
        "fmt"
        "strings"
        "time"
    )
    
    func main() {
    
        t0 := time.Now()
    
        builder := strings.Builder{}
        for i := 0; i < 100_000; i++ {
            builder.WriteString("falcon")
        }
    
        t1 := time.Now()
    
        result := ""
        for i := 0; i < 100_000; i++ {
            result += "falcon"
        }
    
        t2 := time.Now()
    
        fmt.Println(t1.Sub(t0))
        fmt.Println(t2.Sub(t1))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    上面示例对比两种方法,分别连接字符串10万次。运行结果为:

    1.0285ms
    2.8703108s
    
    • 1
    • 2

    其他字符串连接方法

    strings.Join

    Join连接slice/array的元素:

    package main
    
    import (
        "fmt"
        "strings"
    )
    
    func main() {
    
        words := []string{"an", "old", "falcon"}
        msg := strings.Join(words, " ")
    
        fmt.Println(msg)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    fmt.Sprintf

    使用格式连接字符串:

    package main
    
    import "fmt"
    
    func main() {
    
        w1 := "old"
        w2 := "falcon"
    
        msg := fmt.Sprintf("%s %s", w1, w2)
    
        fmt.Println(msg)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    bytes.Buffer

    与strings.Builder原理一致,读者可以查看其源码:

    package main
    
    import (
        "bytes"
        "fmt"
    )
    
    func main() {
    
        var buf bytes.Buffer
    
        buf.WriteString("an ")
        buf.WriteString("old ")
        buf.WriteString("falcon")
    
        fmt.Println(buf.String())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    当然也可以slice实现:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
    
        var s1 = "an"
        var s2 = " old"
        var s3 = " falcon"
    
        msg := make([]byte, 0)
    
        msg = append(msg, []byte(s1)...)
        msg = append(msg, []byte(s2)...)
        msg = append(msg, []byte(s3)...)
    
        fmt.Println(string(msg))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    加号操作符

    最后还在提及下加号操作:

    package main
    
    import "fmt"
    
    func main() {
    
        w1 := "an"
        w2 := " old"
        w3 := " falcon"
    
        msg := w1 + w2 + w3
        fmt.Println(msg)
    
        str := "There are"
    
        str += " three falcons"
        str += " in the sky"
        fmt.Println(str)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    【Arduino TFT】Arduino uzlib库,用于解压gzip流,解析和风天气返回数据
    【JavaScript】声明变量的关键字let、var、const和val的区别
    SpringBoot SpringBoot 运维实用篇 2 配置高级 2.1 临时属性
    文件服务之FTP
    Http代理与socks5代理有何区别?如何选择?(二)
    浅谈Jmeter性能测试流程
    MySql 主从复制 双机热备 笔记
    leetcode:650. 只有两个键的键盘【二维dp】
    Go:关于goroutine及ants的思考
    jquery中的attr()和prop()的区别是什么?
  • 原文地址:https://blog.csdn.net/neweastsun/article/details/127739145