Golang bytes – variable and package

Bytes are a data type in Golang. In this post, we will explore bytes as well as slices of them, the bytes package itself and what can be done with it.

What is a byte variable?

A byte in Go is simply an unsigned 8-bit integer. That means it has a limit of (0 – 255) in numerical range. In Go, a byte can represent a character from a string as well.

Creating a byte variable

The syntax for declaring a byte is really simple all is needed is to declare it as a byte variable and then use it.

var b byte      // a single byte
var ba []byte   // a byte slice

Zero value of a byte

The zero value of a byte is simply zero (0).

package main

import (
	"fmt"
)

func main() {
	var b byte
	fmt.Println(b)  // 0
}

Using bytes with strings

Bytes can be converted to and from strings in Go. This is very useful in certain cases. Here is an example of using bytes along with strings.

package main

import (
	"fmt"
)

func main() {
	var s string = "Hello World"
	sb := []byte(s)
	
	fmt.Println(sb)  // [72 101 108 108 111 32 87 111 114 108 100]
	
	fmt.Println(string(sb)) // Hello World
}

Golang “bytes” package

The “bytes” package implements many useful functions that can be used to manipulate byte slices. It also contains a type Buffer which is an important struct that we will be exploring later on. Here are some of the most useful functions that can be used with byte slices.

1. Compare byte slices

The Compare(b1, b2 []byte) function compares byte slices lexicographically and returns int based on the following ruleset:

bytes.Compare(a, b []byte) // returns -1 if a < b
                           // returns 0 if a == b
                           // returns 1 if a > b

fmt.Println(bytes.Compare([]byte("abc"), []byte("cba")))  // -1

2. Check if a byte slice is a subslice of other

The Contains(s, a byte[]) function is used to check if a slice is subslice of another slice. Here is an example.

fmt.Println(bytes.Contains([]byte("abc"), []byte("a")))  // true

3. Check if a byte slice contains the rune

The ContainsRune(b []byte, r rune)function is used to check whether a byte slice contains a rune.

fmt.Println(bytes.ContainsRune([]byte("xyz"), 'z'))      // true

4. Check for equality of byte slices

The Equal(a, b []byte)function takes two-byte slices and returns true if they are equal.

fmt.Println(bytes.Equal([]byte("xyz"), []byte("abc")))   // false

The function EqualFold checks whether two-byte slices are equal even if they are different cased.

fmt.Println(bytes.EqualFold([]byte("Go"), []byte("go")))  // true

5. The “Fields” function

The Fields function separates a byte slice by whitespace it contains.

fmt.Println(bytes.Fields([]byte("   aaaa          bbb        ccc  ")))      // [[97 97 97 97] [98 98 98] [99 99 99]]
fmt.Printf("%q", bytes.Fields([]byte("   aaaa          bbb        ccc  "))) // ["aaaa" "bbb" "ccc"]

6. The “Index” function

The index function gets the first index of occurrence of a subslice in a slice of bytes. It returns -1 if it cannot find the index.

fmt.Println(bytes.Index([]byte("abcdefghi"), []byte("cd"))) // 2

7. Join byte slices

The join function takes an array of a byte slice and joins them with a separator that it takes as an argument.

s := [][]byte{[]byte("abc"), []byte("def"), []byte("ghi")}
fmt.Printf("%s", bytes.Join(s, []byte(", ")))  // abc, def, ghi

8. The repeat function

The repeat function simply repeats the slice the number of times it gets as an argument.

fmt.Println(string(bytes.Repeat([]byte("abc"), 3))) // abcabcabc

9. The “Split” function

The split function uses the separator to split the string into an array of string.

fmt.Printf("%q\n", bytes.Split([]byte("abababab"), []byte("b")))  // ["a" "a" "a" "a" ""]

10. Trimming the slice

The slice can be trimmed off anything we use by simply using the trim function. Here is the way to do that.

fmt.Printf("%q\n", bytes.Trim([]byte("abcdefghi  "), " "))  // "abcdefghi"

Go bytes.Buffer type

The buffer is a useful struct which is very efficient. The Buffer type comes with the bytes package. Here is the syntax for declaring an empty buffer.

var b bytes.Buffer

To write into a Buffer we can use the write function like this.

b.Write([]byte("a string"))
fmt.Println(b.String())        // a string

The Fprintf can also be used to write into the buffer.

fmt.Fprintf(&b, "some string")

There are some functions that can be used with the Buffer type. This comes useful for many different scenarios. Here we are going to explore some of them.

// gets all the unread portion in that buffer as a slice of bytes of length of the buffer
fmt.Println(b.Bytes())	// [97 32 115 116 114 105 110 103]
	
// the capacity of the buffer
fmt.Println(b.Cap())  // 64
	
// grow the buffer
b.Grow(100)
	
// the length of the buffer
fmt.Println(b.Len())  // 8
	
// cap changes after grow
fmt.Println(b.Cap())  // 228

There are multiple write functions for different types of data like strings, runes, etc in the Buffer type.

b.Write([]byte("a string"))     // Convert to byte slice    
b.WriteString("another string") // Use a string
b.WriteByte(101)                // A byte is an uint8(0 - 255)
b.WriteRune('%')                // A rune

These are some of the most common use cases of bytes in Go.