Go 字节
最后修改于 2025 年 5 月 3 日
在本文中,我们将展示如何在 Golang 中处理字节。
在 Go 中,byte 是一个无符号的 8 位整数,它是 uint8 的类型别名。由于其数值范围为 0-255,因此常用于表示单个 ASCII 字符。在处理原始二进制数据或在字符级别处理文本数据时,byte 值经常被使用。
为了处理多字节字符,Go 提供了 rune 类型,它是 int32 的别名。与表示单字节值的 byte 不同,rune 可以存储 Unicode 码点,使其适用于处理国际文本和复杂的字符集。
为了方便字节级别的操作,Go 提供了 bytes 包,它实现了一系列函数,用于高效地处理字节切片。bytes 包与 strings 包有相似之处,但它操作的是字节切片而不是字符串值。这种区别使其在修改、比较和搜索原始二进制数据等任务中特别有用,同时最大限度地减少了不必要的内存分配。
Go 字节示例
在下一个示例中,我们将处理简单的字节。
package main
import "fmt"
func main() {
var a1 byte = 97
var a2 byte = 98
var a3 byte = 99
fmt.Println(a1)
fmt.Println(a2)
fmt.Println(a3)
fmt.Printf("%c\n", a1)
fmt.Printf("%c\n", a2)
fmt.Printf("%c\n", a3)
}
我们有三个字节。
var a1 byte = 97 var a2 byte = 98 var a3 byte = 99
字节使用 byte 数据类型定义。
fmt.Printf("%c\n", a1)
fmt.Printf("%c\n", a2)
fmt.Printf("%c\n", a3)
使用 %c 格式动词,我们打印字节的字符表示。
$ go run first.go 97 98 99 a b c
我们必须显式地将变量设置为 byte 类型;否则,我们会得到不同的类型。
package main
import (
"fmt"
"reflect"
)
func main() {
var a byte = 97
var b = 98
c := 'c'
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println("-------------------------")
fmt.Printf("%c\n", a)
fmt.Printf("%c\n", b)
fmt.Printf("%c\n", c)
fmt.Println("-------------------------")
fmt.Println(reflect.TypeOf(a))
fmt.Println(reflect.TypeOf(b))
fmt.Println(reflect.TypeOf(c))
}
在代码示例中,我们有三个变量。
var a byte = 97
a 变量具有 byte 数据类型。
var b = 98
由于我们没有显式设置数据类型,Go 会设置默认的 int 类型。
c := 'c'
字符字面量被设置为 rune 类型(int32)。
$ go run types.go 97 98 99 ------------------------- a b c ------------------------- uint8 int int32
Go 字符串转字节
在下面的示例中,我们将字符串转换为字节。
package main
import (
"fmt"
)
func main() {
fmt.Println([]byte("falcon"))
fmt.Println([]byte("čerešňa"))
}
我们使用 []byte() 类型转换将两个字符串转换为字节。
$ go run str2bytes.go [102 97 108 99 111 110] [196 141 101 114 101 197 161 197 136 97]
Go 字节转字符串
在下面的示例中,我们将字节转换为字符串。
package main
import "fmt"
func main() {
data := []byte{102, 97, 108, 99, 111, 110}
fmt.Println(data)
fmt.Println(string(data))
}
我们使用 string 函数将字节切片转换为字符串。
$ go run bytes2str.go [102 97 108 99 111 110] falcon
Go 计算字节数
我们使用 len 函数计算字节数。要计算 rune 数,我们使用 utf8.RuneCountInString 函数。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
msg := "one 🐜"
n1 := len(msg)
n2 := utf8.RuneCountInString(msg)
fmt.Println(n1)
fmt.Println(n2)
}
我们计算 msg 字符串中的字节数和 rune 数。
$ go run counting.go 8 5
有五个 rune 和八个字符。这意味着我们有一个 rune 包含四个字节。
Go 字节读取文件
Go 中的许多内置 I/O 函数都返回一个字节切片。
falcon sky cup oak water
我们读取这个文本文件。
package main
import (
"fmt"
"io/ioutil"
"log"
)
func main() {
content, err := ioutil.ReadFile("words.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(content)
fmt.Println("-------------")
fmt.Println(string(content))
}
ioutil.ReadFile 读取指定文件并将其内容作为字节切片返回。
$ go run read_file.go [102 97 108 99 111 110 10 115 107 121 10 99 117 112 10 111 97 107 10 119 97 116 101 114] ------------- falcon sky cup oak water
Go 字节读取二进制文件
在下面的示例中,我们读取一个二进制文件并以十六进制视图输出。
package main
import (
"bufio"
"encoding/hex"
"fmt"
"io"
"log"
"os"
)
func main() {
f, err := os.Open("favicon.ico")
if err != nil {
log.Fatal(err)
}
defer f.Close()
reader := bufio.NewReader(f)
buf := make([]byte, 256)
for {
_, err := reader.Read(buf)
if err != nil {
if err != io.EOF {
fmt.Println(err)
}
break
}
fmt.Printf("%s", hex.Dump(buf))
}
}
hex.Dump 函数返回一个包含给定数据十六进制转储的字符串。十六进制转储的格式与 hexdump -C Unix 命令的输出相匹配。
$ go run read_binary.go 00000000 00 00 01 00 01 00 10 10 00 00 00 00 00 00 68 05 |..............h.| 00000010 00 00 16 00 00 00 28 00 00 00 10 00 00 00 20 00 |......(....... .| 00000020 00 00 01 00 08 00 00 00 00 00 00 01 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 |................| 00000040 00 00 ff ff ff 00 4d 45 3d 00 00 00 00 00 00 00 |......ME=.......| 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ...
Go 转义字节
任意字符值都可以用反斜杠转义来编码,并在字符串或 rune 字面量中使用。Go 支持一种常见的格式,其中字节表示为 \x 后面跟着两个十六进制值。
package main
import (
"fmt"
)
func main() {
fmt.Println("\xF0\x9F\x92\xBF")
fmt.Println("\xF0\x9F\x8E\xB2")
fmt.Println("\xF0\x9F\x90\xA8")
fmt.Println("\xF0\x9F\x90\xA7")
fmt.Println("\xF0\x9F\x90\xAB")
fmt.Println("\xF0\x9F\x90\xAC")
}
在代码示例中,我们打印了六个 emoji 字符。这些 emoji 是通过转义字节指定的。
$ go run escaped_bytes.go 💿 🎲 🐨 🐧 🐫 🐬
Go 字节函数
bytes 包包含用于操作字节切片的函数。
package main
import (
"bytes"
"fmt"
)
func main() {
data1 := []byte{102, 97, 108, 99, 111, 110} // falcon
data2 := []byte{111, 110} // on
if bytes.Contains(data1, data2) {
fmt.Println("contains")
} else {
fmt.Println("does not contain")
}
if bytes.Equal([]byte("falcon"), []byte("owl")) {
fmt.Println("equal")
} else {
fmt.Println("not equal")
}
data3 := []byte{111, 119, 108, 9, 99, 97, 116, 32, 32, 32, 32, 100, 111,
103, 32, 112, 105, 103, 32, 32, 32, 32, 98, 101, 97, 114}
fields := bytes.Fields(data3)
fmt.Println(fields)
for _, e := range fields {
fmt.Printf("%s ", string(e))
}
fmt.Println()
}
在代码示例中,我们使用了 Contains、Equal 和 Fields 函数。
if bytes.Contains(data1, data2) {
fmt.Println("contains")
} else {
fmt.Println("does not contain")
}
使用 Contains,我们检查 data2 切片是否是 data1 的子切片。
if bytes.Equal([]byte("falcon"), []byte("owl")) {
fmt.Println("equal")
} else {
fmt.Println("not equal")
}
使用 Equal,我们检查两个切片是否具有相同的长度并包含相同的字节。
fields := bytes.Fields(data3)
Fields 函数将字节切片分割成子切片,去除任何空格字符,包括换行符。
$ go run byte_funs.go contains not equal [[111 119 108] [99 97 116] [100 111 103] [112 105 103] [98 101 97 114]] owl cat dog pig bear
在下一个示例中,我们使用了另外三个函数。
package main
import (
"bytes"
"fmt"
)
func main() {
data := [][]byte{[]byte("an"), []byte("old"), []byte("wolf")}
joined := bytes.Join(data, []byte(" "))
fmt.Println(data)
fmt.Println(joined)
fmt.Println(string(joined))
fmt.Println("--------------------------")
data2 := []byte{102, 97, 108, 99, 111, 110, 32}
data3 := bytes.Repeat(data2, 3)
fmt.Println(data3)
fmt.Println(string(data3))
fmt.Println("--------------------------")
data4 := []byte{32, 32, 102, 97, 108, 99, 111, 110, 32, 32, 32}
data5 := bytes.Trim(data4, " ")
fmt.Println(data5)
fmt.Println(string(data5))
}
该示例使用 Join 连接字节切片,使用 Repeat 重复字节切片,并使用 Trim 修剪字节切片中的指定字节。
$ go run byte_funs2.go [[97 110] [111 108 100] [119 111 108 102]] [97 110 32 111 108 100 32 119 111 108 102] an old wolf -------------------------- [102 97 108 99 111 110 32 102 97 108 99 111 110 32 102 97 108 99 111 110 32] falcon falcon falcon -------------------------- [102 97 108 99 111 110] falcon
Go bytes.Buffer
bytes.Buffer 是一个可变大小的字节缓冲区,具有 Read 和 Write 方法。
package main
import (
"bytes"
"fmt"
)
func main() {
var buf bytes.Buffer
buf.Write([]byte("a old"))
buf.WriteByte(32)
buf.WriteString("cactus")
buf.WriteByte(32)
buf.WriteByte(32)
buf.WriteRune('🌵')
fmt.Println(buf)
fmt.Println(buf.String())
}
我们使用 Write、WriteByte、WriteString、WriteByte 和 WriteRune 方法构建了一个 bytes.Buffer。
$ go run buffer.go
{[97 32 111 108 100 32 99 97 99 116 117 115 32 32 240 159 140 181] 0 0}
a old cactus 🌵
来源
在本文中,我们已经在 Golang 中处理了字节。