我们在日常开发中经常要用到字符串拼接,Golang提供了多种拼接方法。常用有以下几种:

  • “+” operator
  • fmt.Sprintf
  • bytes.Buffer
  • strings.Builder

一,Benchmark 比较

下表是在VMware VM(centos7)上做的测试,拼接一个字符串包含1000个字符x。

  1. BenchmarkSimple-2 7265664 164 ns/op 530 B/op 0 allocs/op
  2. BenchmarkSprintf-2 4183986 298 ns/op 546 B/op 1 allocs/op
  3. BenchmarkBytesBuffer-2 90349341 11.9 ns/op 2 B/op 0 allocs/op
  4. BenchmarkStringBuilder-2 370823845 3.23 ns/op 2 B/op 0 allocs/op

我们可以得出以下结果:

  • 从数量级上看,bytes.Buffer (BenchmarkBytesBuffer-2) 和 strings.Builder (BenchmarkStringBuilder-2) 要优于 “+” operator (BenchmarkSimple-2) 和 fmt.Sprintf (BenchmarkSprintf-2)

  • 细微上看,strings.Builder (BenchmarkStringBuilder-2) 优于 bytes.Buffer (BenchmarkBytesBuffer-2)。“+” operator (BenchmarkSimple-2) 优于 fmt.Sprintf (BenchmarkSprintf-2)。

对待一些简单得场景,“+” operator足够好用。而一些复杂字符串的拼接,strings.Builder 完全可以替代 fmt.Sprintf,提高性能。

二,bytes.Buffer

有时候我们不仅仅是拼接字符串,而是字节“串”,例如文件IO,网络IO等。bytes.Buffer 绝对是一个最佳选择,提供丰富易用的函数集。
主要的数据结构:

  1. // A Buffer is a variable-sized buffer of bytes with Read and Write methods.
  2. // The zero value for Buffer is an empty buffer ready to use.
  3. type Buffer struct {
  4. buf []byte // contents are the bytes buf[off : len(buf)]
  5. off int // read at &buf[off], write at &buf[len(buf)]
  6. lastRead readOp // last read operation, so that Unread* can work correctly.
  7. }

建立新 Buffer - 没有初始值

  1. new(bytes.Buffer)

建立新 Buffer - 有初始值

  1. // NewBuffer creates and initializes a new Buffer using buf as its
  2. // initial contents. The new Buffer takes ownership of buf, and the
  3. // caller should not use buf after this call. NewBuffer is intended to
  4. // prepare a Buffer to read existing data. It can also be used to set
  5. // the initial size of the internal buffer for writing. To do that,
  6. // buf should have the desired capacity but a length of zero.
  7. //
  8. // In most cases, new(Buffer) (or just declaring a Buffer variable) is
  9. // sufficient to initialize a Buffer.
  10. func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }
  11. // NewBufferString creates and initializes a new Buffer using string s as its
  12. // initial contents. It is intended to prepare a buffer to read an existing
  13. // string.
  14. //
  15. // In most cases, new(Buffer) (or just declaring a Buffer variable) is
  16. // sufficient to initialize a Buffer.
  17. func NewBufferString(s string) *Buffer {
  18. return &Buffer{buf: []byte(s)}
  19. }

写 Buffer

  1. // Write appends the contents of p to the buffer, growing the buffer as
  2. // needed. The return value n is the length of p; err is always nil. If the
  3. // buffer becomes too large, Write will panic with ErrTooLarge.
  4. func (b *Buffer) Write(p []byte) (n int, err error) {
  5. b.lastRead = opInvalid
  6. m, ok := b.tryGrowByReslice(len(p))
  7. if !ok {
  8. m = b.grow(len(p))
  9. }
  10. return copy(b.buf[m:], p), nil
  11. }
  12. // WriteString appends the contents of s to the buffer, growing the buffer as
  13. // needed. The return value n is the length of s; err is always nil. If the
  14. // buffer becomes too large, WriteString will panic with ErrTooLarge.
  15. func (b *Buffer) WriteString(s string) (n int, err error) {
  16. b.lastRead = opInvalid
  17. m, ok := b.tryGrowByReslice(len(s))
  18. if !ok {
  19. m = b.grow(len(s))
  20. }
  21. return copy(b.buf[m:], s), nil
  22. }
  23. // WriteByte appends the byte c to the buffer, growing the buffer as needed.
  24. // The returned error is always nil, but is included to match bufio.Writer's
  25. // WriteByte. If the buffer becomes too large, WriteByte will panic with
  26. // ErrTooLarge.
  27. func (b *Buffer) WriteByte(c byte) error {
  28. b.lastRead = opInvalid
  29. m, ok := b.tryGrowByReslice(1)
  30. if !ok {
  31. m = b.grow(1)
  32. }
  33. b.buf[m] = c
  34. return nil
  35. }
  36. // WriteRune appends the UTF-8 encoding of Unicode code point r to the
  37. // buffer, returning its length and an error, which is always nil but is
  38. // included to match bufio.Writer's WriteRune. The buffer is grown as needed;
  39. // if it becomes too large, WriteRune will panic with ErrTooLarge.
  40. func (b *Buffer) WriteRune(r rune) (n int, err error) {
  41. if r < utf8.RuneSelf {
  42. b.WriteByte(byte(r))
  43. return 1, nil
  44. }
  45. b.lastRead = opInvalid
  46. m, ok := b.tryGrowByReslice(utf8.UTFMax)
  47. if !ok {
  48. m = b.grow(utf8.UTFMax)
  49. }
  50. n = utf8.EncodeRune(b.buf[m:m+utf8.UTFMax], r)
  51. b.buf = b.buf[:m+n]
  52. return n, nil
  53. }
  54. // WriteTo writes data to w until the buffer is drained or an error occurs.
  55. // The return value n is the number of bytes written; it always fits into an
  56. // int, but it is int64 to match the io.WriterTo interface. Any error
  57. // encountered during the write is also returned.
  58. func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
  59. b.lastRead = opInvalid
  60. if nBytes := b.Len(); nBytes > 0 {
  61. m, e := w.Write(b.buf[b.off:])
  62. if m > nBytes {
  63. panic("bytes.Buffer.WriteTo: invalid Write count")
  64. }
  65. b.off += m
  66. n = int64(m)
  67. if e != nil {
  68. return n, e
  69. }
  70. // all bytes should have been written, by definition of
  71. // Write method in io.Writer
  72. if m != nBytes {
  73. return n, io.ErrShortWrite
  74. }
  75. }
  76. // Buffer is now empty; reset.
  77. b.Reset()
  78. return n, nil
  79. }

读 Buffer

  1. // Read reads the next len(p) bytes from the buffer or until the buffer
  2. // is drained. The return value n is the number of bytes read. If the
  3. // buffer has no data to return, err is io.EOF (unless len(p) is zero);
  4. // otherwise it is nil.
  5. func (b *Buffer) Read(p []byte) (n int, err error) {
  6. b.lastRead = opInvalid
  7. if b.empty() {
  8. // Buffer is empty, reset to recover space.
  9. b.Reset()
  10. if len(p) == 0 {
  11. return 0, nil
  12. }
  13. return 0, io.EOF
  14. }
  15. n = copy(p, b.buf[b.off:])
  16. b.off += n
  17. if n > 0 {
  18. b.lastRead = opRead
  19. }
  20. return n, nil
  21. }
  22. // Next returns a slice containing the next n bytes from the buffer,
  23. // advancing the buffer as if the bytes had been returned by Read.
  24. // If there are fewer than n bytes in the buffer, Next returns the entire buffer.
  25. // The slice is only valid until the next call to a read or write method.
  26. func (b *Buffer) Next(n int) []byte {
  27. b.lastRead = opInvalid
  28. m := b.Len()
  29. if n > m {
  30. n = m
  31. }
  32. data := b.buf[b.off : b.off+n]
  33. b.off += n
  34. if n > 0 {
  35. b.lastRead = opRead
  36. }
  37. return data
  38. }
  39. // ReadByte reads and returns the next byte from the buffer.
  40. // If no byte is available, it returns error io.EOF.
  41. func (b *Buffer) ReadByte() (byte, error) {
  42. if b.empty() {
  43. // Buffer is empty, reset to recover space.
  44. b.Reset()
  45. return 0, io.EOF
  46. }
  47. c := b.buf[b.off]
  48. b.off++
  49. b.lastRead = opRead
  50. return c, nil
  51. }
  52. // ReadRune reads and returns the next UTF-8-encoded
  53. // Unicode code point from the buffer.
  54. // If no bytes are available, the error returned is io.EOF.
  55. // If the bytes are an erroneous UTF-8 encoding, it
  56. // consumes one byte and returns U+FFFD, 1.
  57. func (b *Buffer) ReadRune() (r rune, size int, err error) {
  58. if b.empty() {
  59. // Buffer is empty, reset to recover space.
  60. b.Reset()
  61. return 0, 0, io.EOF
  62. }
  63. c := b.buf[b.off]
  64. if c < utf8.RuneSelf {
  65. b.off++
  66. b.lastRead = opReadRune1
  67. return rune(c), 1, nil
  68. }
  69. r, n := utf8.DecodeRune(b.buf[b.off:])
  70. b.off += n
  71. b.lastRead = readOp(n)
  72. return r, n, nil
  73. }
  74. // UnreadRune unreads the last rune returned by ReadRune.
  75. // If the most recent read or write operation on the buffer was
  76. // not a successful ReadRune, UnreadRune returns an error. (In this regard
  77. // it is stricter than UnreadByte, which will unread the last byte
  78. // from any read operation.)
  79. func (b *Buffer) UnreadRune() error {
  80. if b.lastRead <= opInvalid {
  81. return errors.New("bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune")
  82. }
  83. if b.off >= int(b.lastRead) {
  84. b.off -= int(b.lastRead)
  85. }
  86. b.lastRead = opInvalid
  87. return nil
  88. }
  89. var errUnreadByte = errors.New("bytes.Buffer: UnreadByte: previous operation was not a successful read")
  90. // UnreadByte unreads the last byte returned by the most recent successful
  91. // read operation that read at least one byte. If a write has happened since
  92. // the last read, if the last read returned an error, or if the read read zero
  93. // bytes, UnreadByte returns an error.
  94. func (b *Buffer) UnreadByte() error {
  95. if b.lastRead == opInvalid {
  96. return errUnreadByte
  97. }
  98. b.lastRead = opInvalid
  99. if b.off > 0 {
  100. b.off--
  101. }
  102. return nil
  103. }
  104. // ReadBytes reads until the first occurrence of delim in the input,
  105. // returning a slice containing the data up to and including the delimiter.
  106. // If ReadBytes encounters an error before finding a delimiter,
  107. // it returns the data read before the error and the error itself (often io.EOF).
  108. // ReadBytes returns err != nil if and only if the returned data does not end in
  109. // delim.
  110. func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
  111. slice, err := b.readSlice(delim)
  112. // return a copy of slice. The buffer's backing array may
  113. // be overwritten by later calls.
  114. line = append(line, slice...)
  115. return line, err
  116. }
  117. // ReadString reads until the first occurrence of delim in the input,
  118. // returning a string containing the data up to and including the delimiter.
  119. // If ReadString encounters an error before finding a delimiter,
  120. // it returns the data read before the error and the error itself (often io.EOF).
  121. // ReadString returns err != nil if and only if the returned data does not end
  122. // in delim.
  123. func (b *Buffer) ReadString(delim byte) (line string, err error) {
  124. slice, err := b.readSlice(delim)
  125. return string(slice), err
  126. }

置零 Buffer

  1. // Reset resets the buffer to be empty,
  2. // but it retains the underlying storage for use by future writes.
  3. // Reset is the same as Truncate(0).
  4. func (b *Buffer) Reset() {
  5. b.buf = b.buf[:0]
  6. b.off = 0
  7. b.lastRead = opInvalid
  8. }

导出 Buffer

  1. // Bytes returns a slice of length b.Len() holding the unread portion of the buffer.
  2. // The slice is valid for use only until the next buffer modification (that is,
  3. // only until the next call to a method like Read, Write, Reset, or Truncate).
  4. // The slice aliases the buffer content at least until the next buffer modification,
  5. // so immediate changes to the slice will affect the result of future reads.
  6. func (b *Buffer) Bytes() []byte { return b.buf[b.off:] }
  7. // String returns the contents of the unread portion of the buffer
  8. // as a string. If the Buffer is a nil pointer, it returns "<nil>".
  9. //
  10. // To build strings more efficiently, see the strings.Builder type.
  11. func (b *Buffer) String() string {
  12. if b == nil {
  13. // Special case, useful in debugging.
  14. return "<nil>"
  15. }
  16. return string(b.buf[b.off:])
  17. }
  18. // Len returns the number of bytes of the unread portion of the buffer;
  19. // b.Len() == len(b.Bytes()).
  20. func (b *Buffer) Len() int { return len(b.buf) - b.off }
  21. // Cap returns the capacity of the buffer's underlying byte slice, that is, the
  22. // total space allocated for the buffer's data.
  23. func (b *Buffer) Cap() int { return cap(b.buf) }

一个例子:来自以太坊 Discovery 中的 Packet Encode 代码。

  1. // Encode encodes a discovery packet.
  2. func Encode(priv *ecdsa.PrivateKey, req Packet) (packet, hash []byte, err error) {
  3. b := new(bytes.Buffer)
  4. b.Write(headSpace)
  5. b.WriteByte(req.Kind())
  6. if err := rlp.Encode(b, req); err != nil {
  7. return nil, nil, err
  8. }
  9. packet = b.Bytes()
  10. sig, err := crypto.Sign(crypto.Keccak256(packet[headSize:]), priv)
  11. if err != nil {
  12. return nil, nil, err
  13. }
  14. copy(packet[macSize:], sig)
  15. // Add the hash to the front. Note: this doesn't protect the packet in any way.
  16. hash = crypto.Keccak256(packet[macSize:])
  17. copy(packet, hash)
  18. return packet, hash, nil
  19. }

参考链接: