目录

Go 连接字符串

Go 中连接字符串的方法有很多种,其实掌握一两种较为高效的方法就足够了,其它权当看热闹。

几种连接方式

Concat

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package hello

import "testing"

/*
=== RUN   TestStringConcat
    /Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:20: Hello World!
--- PASS: TestStringConcat (0.00s)
PASS
ok  	example.com/hello	0.014s
*/
func TestStringConcat(t *testing.T) {
  s := "Hello "
  s += "World!"
  t.Log(s)
}

Sprintf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package hello

import (
  "fmt"
  "testing"
)

/*
=== RUN   TestStringSprintf
    /Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: Hello World!
--- PASS: TestStringSprintf (0.00s)
PASS
ok  	example.com/hello	0.010s
*/
func TestStringSprintf(t *testing.T) {
  s := "Hello "
  s = fmt.Sprintf("%s%s", s, "World!")
  t.Log(s)
}

Join

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package hello

import (
  "strings"
  "testing"
)

/*
=== RUN   TestStringJoin
    /Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: Hello World!
--- PASS: TestStringJoin (0.00s)
PASS
ok  	example.com/hello	0.008s
*/
func TestStringJoin(t *testing.T) {
  s := "Hello "
  s = strings.Join([]string{s, "World!"}, "")
  t.Log(s)
}

Buffer Write

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package hello

import (
  "bytes"
  "testing"
)

/*
=== RUN   TestBufferWrite
    /Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:20: Hello World!
--- PASS: TestBufferWrite (0.00s)
PASS
ok  	example.com/hello	0.010s
*/
func TestBufferWrite(t *testing.T) {
  buf := new(bytes.Buffer)
  buf.WriteString("Hello ")
  buf.WriteString("World!")
  s := buf.String()
  t.Log(s)
}

Bytes Append

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package hello

import "testing"

/*
=== RUN   TestBytesAppend
    /Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: Hello
--- PASS: TestBytesAppend (0.00s)
PASS
ok  	example.com/hello
*/
func TestBytesAppend(t *testing.T) {
  var b []byte
  s := "Hello "
  b = append(b, "World!"...)
  t.Log(string(s))
}

Copy

 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
package hello

import "testing"

/*
=== RUN   TestStringCopy
    /Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:18: Hello World!
--- PASS: TestStringCopy (0.00s)
PASS
ok  	example.com/hello	(cached)
*/
func TestStringCopy(t *testing.T) {
  b := make([]byte, 0)
  b = mycopy(b, "Hello ")
  b = mycopy(b, "World!")
  t.Log(string(b))
}

func mycopy(b []byte, s string) []byte {
  n := len(b)
  if n+len(s) > cap(b) {
    t := make([]byte, 2*cap(b)+len(s))
    copy(t, b)
    b = t
  }
  b = b[0 : n+len(s)]
  copy(b[n:], s)
  return b
}

String Builder

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package hello

import (
  "strings"
  "testing"
)

/*
=== RUN   TestStringBuilder
    /Users/xxx/Codes/go/src/github.com/xxx/hello/hello_test.go:20: Hello World!
--- PASS: TestStringBuilder (0.00s)
PASS
ok  	example.com/hello	0.013s
*/
func TestStringBuilder(t *testing.T) {
  var builder strings.Builder
  builder.WriteString("Hello ")
  builder.WriteString("World!")
  s := builder.String()
  t.Log(s)
}

性能测试

  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
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package hello

import (
  "bytes"
  "fmt"
  "strings"
  "testing"
)

const (
  src = "Hello World!"
  cnt = 10000
)

var expected = strings.Repeat(src, cnt)

func mycopy(b []byte, s string) []byte {
  n := len(b)
  if n+len(s) > cap(b) {
    t := make([]byte, 2*cap(b)+len(s))
    copy(t, b)
    b = t
  }
  b = b[0 : n+len(s)]
  copy(b[n:], s)
  return b
}

func BenchmarkStringConcat(b *testing.B) {
  var res string
  for n := 0; n < b.N; n++ {
    var str string
    for i := 0; i < cnt; i++ {
      str += src
    }
    res = str
  }
  b.StopTimer()
  if res != expected {
    b.Errorf("got=%s, want=%s", string(res), expected)
  }
}

func BenchmarkStringSprintf(b *testing.B) {
  var res string
  for n := 0; n < b.N; n++ {
    var str string
    for i := 0; i < cnt; i++ {
      str = fmt.Sprintf("%s%s", str, src)
    }
    res = str
  }
  b.StopTimer()
  if res != expected {
    b.Errorf("got=%s, want=%s", string(res), expected)
  }
}

func BenchmarkStringJoin(b *testing.B) {
  var res string
  for n := 0; n < b.N; n++ {
    var str string
    for i := 0; i < cnt; i++ {
      str = strings.Join([]string{str, src}, "")
    }
    res = str
  }
  b.StopTimer()
  if res != expected {
    b.Errorf("got=%s, want=%s", res, expected)
  }
}

func BenchmarkBufferWrite(b *testing.B) {
  var res string
  for n := 0; n < b.N; n++ {
    buf := new(bytes.Buffer)
    for i := 0; i < cnt; i++ {
      buf.WriteString(src)
    }
    res = buf.String()
  }
  b.StopTimer()
  if res != expected {
    b.Errorf("got=%s, want=%s", string(res), expected)
  }
}

func BenchmarkBytesAppend(b *testing.B) {
  var res string
  for n := 0; n < b.N; n++ {
    var bytes []byte
    for i := 0; i < cnt; i++ {
      bytes = append(bytes, src...)
    }
    res = string(bytes)
  }
  b.StopTimer()
  if res != expected {
    b.Errorf("got=%s, want=%s", string(res), expected)
  }
}

func BenchmarkStringCopy(b *testing.B) {
  var res string
  for n := 0; n < b.N; n++ {
    data := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
    for i := 0; i < cnt; i++ {
      data = mycopy(data, src)
    }
    res = string(data)
  }
  b.StopTimer()
  if res != expected {
      b.Errorf("got=%s, want=%s", string(res), expected)
  }
}

func BenchmarkStringCopy2(b *testing.B) {
  var res string
  for n := 0; n < b.N; n++ {
    b := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
    for i := 0; i < cnt; i++ {
      l := len(b)
      if l+len(src) > cap(b) {
        t := make([]byte, 2*cap(b)+len(src))
        copy(t, b)
        b = t
      }
      b = b[0 : l+len(src)]
      copy(b[l:], src)
    }
    res = string(b)
  }
  b.StopTimer()
  if res != expected {
    b.Errorf("got=%s, want=%s", string(res), expected)
  }
}

func BenchmarkStringBuilder(b *testing.B) {
  var res string
  for n := 0; n < b.N; n++ {
    var builder strings.Builder
    for i := 0; i < cnt; i++ {
      builder.WriteString(src)
    }
    res = builder.String()
  }
  b.StopTimer()
  if res != expected {
    b.Errorf("got=%s, want=%s", string(res), expected)
  }
}

在我本机的测试结果如下,对于这个测试结果我表示也没想到,但是我已经知道以后该怎么做了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ go test -v -benchmem -run=^$ -bench '^(BenchmarkStringConcat|BenchmarkStringSprintf|BenchmarkStringJoin|BenchmarkBufferWrite|BenchmarkBytesAppend|BenchmarkStringCopy|BenchmarkStringCopy2|BenchmarkStringBuilder)$' example.com/hello
goos: darwin
goarch: amd64
pkg: example.com/hello
cpu: Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
BenchmarkStringConcat
BenchmarkStringConcat-4                8         131090517 ns/op        632845121 B/op     10004 allocs/op
BenchmarkStringSprintf
BenchmarkStringSprintf-4               4         295268326 ns/op        1075561174 B/op    29686 allocs/op
BenchmarkStringJoin
BenchmarkStringJoin-4                  6         287073860 ns/op        632845282 B/op     10008 allocs/op
BenchmarkBufferWrite
BenchmarkBufferWrite-4              5631            210591 ns/op          441616 B/op         13 allocs/op
BenchmarkBytesAppend
BenchmarkBytesAppend-4              7819            152409 ns/op          628720 B/op         25 allocs/op
BenchmarkStringCopy
BenchmarkStringCopy-4               8820            118761 ns/op          441552 B/op         12 allocs/op
BenchmarkStringCopy2
BenchmarkStringCopy2-4              9598            135427 ns/op          441552 B/op         12 allocs/op
BenchmarkStringBuilder
BenchmarkStringBuilder-4            7729            156815 ns/op          505840 B/op         24 allocs/op
PASS
ok      example.com/hello       17.512s