As a common data structure, buffers (buffers) have wide applications in computer science. The Go language standard library provides a buffer type called , which can conveniently perform string operations, IO operations, binary data processing, etc. This blog will introduce in detail the usage of Buffer in Go, and introduce its features and application scenarios from multiple aspects.
1. What is a Buffer
In computer science, a buffer is a data structure that is used to temporarily store data for processing later. In Go, it is a predefined type that stores and manipulates sequences of bytes. Types provide many useful methods, such as reading and writing bytes, strings, integers, and floating point numbers.
// Create an empty buffervar buf // Write string to the buffer("Hello, World!") // Read string from buffer(()) // Output:Hello, World!
2. Create a buffer
To use the Buffer type, we first need to create a buffer. There are two ways to create a Buffer object.
2.1 Create using the NewBuffer function
You can use the NewBuffer function in the bytes package to create a new buffer object. Its method is as follows:
func NewBuffer(buf []byte) *Buffer
Where, the buf parameter is optional, which can be used to specify the initial capacity of the buffer. If this parameter is not specified, a buffer with a default capacity of 64 bytes is created.
Here is an example of using the NewBuffer function to create a buffer:
import ( "bytes" "fmt" ) func main() { buf := ("hello world") (()) // Output: hello world}
2.2 Create using structure
Another way to create a buffer object is to directly declare a variable of type. This method is relatively simple, but it should be noted that if the buffer created in this method is not initialized, its initial capacity is 0 and it needs to be expanded before writing data.
Here is an example of using a structure to create a buffer:
import ( "bytes" "fmt" ) func main() { var buf ("hello") (" ") ("world") (()) // Output: hello world}
3. Write data
After creating the buffer, we can write data to it. The Buffer type provides a variety of methods to write data, the most commonly used is the Write method. Its method is as follows:
func (b *Buffer) Write(p []byte) (n int, err error)
Among them, the p parameter is the byte slice to be written to the buffer, the return value n represents the actual number of bytes written, and err represents an error that may occur during the writing process.
In addition to the Write method, the Buffer type also provides a series of other methods to write data, such as WriteString, WriteByte, WriteRune, etc. These methods are used to write strings, single bytes, single Unicode characters, etc. to the buffer respectively.
Here is an example of using the Write method to write data to a buffer:
import ( "bytes" "fmt" ) func main() { buf := (nil) n, err := ([]byte("hello world")) if err != nil { ("write error:", err) } ("write %d bytes\n", n) // Output: write 11 bytes (()) // Output: hello world}
4. Read data
In addition to writing data, we can also read data from the buffer. The Buffer type provides a variety of ways to read data, the most commonly used is the Read method. Its method is as follows:
func (b *Buffer) Read(p []byte) (n int, err error)
Among them, the p parameter is a byte slice used to store the read data, the return value n represents the actual number of bytes read, and err represents an error that may occur during the reading process.
In addition to the Read method, the Buffer type also provides a series of other methods to read data, such as ReadString, ReadByte, ReadRune, etc. These methods are used to read strings, single bytes, single Unicode characters, etc. from the buffer respectively.
Here is an example of using the Read method to read data from a buffer:
import ( "bytes" "fmt" ) func main() { buf := ("hello world") data := make([]byte, 5) n, err := (data) if err != nil { ("read error:", err) } ("read %d bytes\n", n) // Output: read 5 bytes (string(data)) // Output: hello}
5. Intercept the buffer
The Buffer type provides the Bytes method and the String method to convert the contents of a buffer into byte slices and strings. In addition, you can also use the Truncate method to intercept the contents of the buffer. Its method is as follows:
func (b *Buffer) Truncate(n int)
Where, the n parameter represents the number of bytes to be retained. If the content length of the buffer exceeds n, it will be intercepted from the tail, leaving only the previous n bytes. If the content length of the buffer is less than n, no operation is done.
Here is an example of using the Truncate method to intercept a buffer:
import ( "bytes" "fmt" ) func main() { buf := ("hello world") (5) (()) // Output: hello}
6. Expansion buffer
During the process of writing data, if the buffer capacity is not enough, it needs to be expanded. The Buffer type provides the Grow method to expand the buffer. Its method is as follows:
func (b *Buffer) Grow(n int)
Where, the n parameter represents the number of bytes to be expanded. If n is less than or equal to the remaining capacity of the buffer, no operation is done. Otherwise, the capacity of the buffer will be expanded to 2 times the original or n is added, taking the larger value of the two.
Here is an example of using the Grow method to expand the buffer:
import ( "bytes" "fmt" ) func main() { buf := ("hello") (10) ("len=%d, cap=%d\n", (), ()) // Output: len=5, cap=16}
In the example above, we created a 5-byte buffer and expanded its capacity to 16 bytes using the Grow method. Since 16 is the smallest integer power of 2 greater than 5, the expanded capacity is 16.
It should be noted that the Buffer type does not guarantee that the expanded buffer is continuous. Therefore, when passing the content of the buffer to an interface that requires continuous memory, the content of the buffer needs to be copied to a new continuous memory.
7. Reset the buffer
In some cases, we need to reuse a buffer. At this point, you can use the Reset method to clear the buffer and reset it to the initial state. Its method is as follows:
func (b *Buffer) Reset()
Here is an example of resetting the buffer using the Reset method:
import ( "bytes" "fmt" ) func main() { buf := ("hello") (()) // Output: hello () (()) // Output:}
In the example above, we first create a buffer containing hello and reset it to an empty buffer using the Reset method. Note that the buffer length and capacity after reset have both become 0.
8. Serialization and deserialization
Since the type supports read and write operations, it can be used to serialize and deserialize structures, JSON, XML and other data formats. This makes the application of types more convenient in network communication and distributed systems.
type Person struct { Name string Age int } // Encode the structure into JSONp := Person{"Alice", 25} enc := (&buf) (p) (()) // Output: {"Name":"Alice","Age":25} // Decode from JSON to structurevar p2 Person dec := (&buf) (&p2) ("Name: %s, Age: %d\n", , ) // Output:Name: Alice, Age: 25
9. Application scenarios of Buffer
9.1 Network communication
In network communication, it can be used to store and process data such as TCP/UDP packets, HTTP requests and responses. For example, we can use types to construct HTTP requests and responses:
// Construct HTTP requestreq := ("GET / HTTP/1.0\r\n\r\n") // Construct HTTP responseresp := ([]byte("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\nHello, World!"))
9.2 File Operation
In file operations, it can be used to cache file contents to avoid frequent disk read and write operations. For example, we can use the type to read and write files:
// Read data from the filefile, err := ("") if err != nil { (err) } defer () var buf _, err = (&buf, file) if err != nil { (err) } (()) // Write data to a fileout, err := ("") if err != nil { (err) } defer () _, err = (out, &buf) if err != nil { (err) }
9.3 Binary data processing
When processing binary data, it can be used to store and manipulate byte arrays. For example, we can use types to read and write byte arrays, convert size and endianness of byte arrays, etc.:
// Read byte arraydata := []byte{0x48, 0x65,0x6c, 0x6c, 0x6f} var buf (data) // Convert size and end ordervar num uint16 (&buf, , &num) (num) // Output: 0x4865 // Write to byte arraydata2 := []byte{0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21} (data2) (()) // Output:[72 101 108 108 111 87 111 114 108 100 33]
9.4 String stitching
When string splicing, if the + operator is used directly, a large number of intermediate variables will be generated, affecting the efficiency of the program. Using the Buffer type can avoid this problem.
import ( "bytes" "strings" ) func concatStrings(strs ...string) string { var buf for _, s := range strs { (s) } return () } func main() { s1 := "hello" s2 := "world" s3 := "!" s := concatStrings(s1, s2, s3) (s) // Output: hello world!}
In the example above, we use the Buffer type to splice multiple strings into one string. Since the Buffer type expands dynamically, it can avoid the generation of a large number of intermediate variables and improve the efficiency of the program.
9.5 Format output
When outputting formatted strings, we can use the function or the Buffer type.
import ( "bytes" "fmt" ) func main() { var buf for i := 0; i < 10; i++ { (&buf, "%d\n", i) } (()) }
In the example above, we format 10 integers into strings using the Buffer type and output them to standard output. Using the Buffer type can easily organize formatted strings, and at the same time, it can also reduce the number of system calls and improve the efficiency of the program.
9.6 Image processing
In image processing, we often need to synthesize multiple images into a new image. Use the Buffer type to conveniently cache pixel values for multiple images and then combine them into a new image.
import ( "bytes" "image" "image/png" "os" ) func combineImages(images []) { width := images[0].Bounds().Dx() height := images[0].Bounds().Dy() * len(images) canvas := ((0, 0, width, height)) var y int for _, img := range images { for i := 0; i < ().Dy(); i++ { for j := 0; j < ().Dx(); j++ { (j, y+i, (j, i)) } } y += ().Dy() } return canvas } func main() { images := make([], 3) for i := 0; i < 3; i++ { f, _ := (("image%", i+1)) img, _ := (f) images[i] = img } combined := combineImages(images) f, _ := ("") (f, combined) }
In the example above, we use the Buffer type to cache the pixel values of multiple images and combine them into a new image. Using the Buffer type can easily cache pixel values, while also reducing the number of system calls and improving program efficiency.
10. Summary
In Go language, type is a very practical data type, which can be used to store and operate binary data, network communication data, file data, etc. In actual development, we often use types to cache data, serialize and deserialize data, process binary data, etc. to improve the readability, maintainability and scalability of the code.
In addition to types, there are also types in Go. They are both implemented based on types and can be used to read and write data, but types can only read data, while types can only write data. In actual development, we can choose different types according to different needs.
The above is an article that will help you learn more about the buffer buffer in Golang. For more information about Golang buffer buffer, please follow my other related articles!