golang的encoding/binary包配合切片可用于高效處理二進制數據。1. 使用binary.bigendian或binary.littleendian實現字節序轉換,通過binary.write和binary.read指定字節序進行寫入與讀取;2. 處理變長數據時,先寫入/讀取長度字段,再操作實際數據;3. 用Struct定義二進制結構時,結合固定大小的byte數組和binary.littleendian或binary.bigendian完成數據序列化與反序列化,并注意對字符串截斷處理空字節。
操作二進制數據,說白了,就是在字節層面“玩耍”。golang提供了強大的encoding/binary包,再加上切片(slice)的靈活運用,讓你能夠自如地讀取、寫入、轉換各種二進制格式的數據。掌握它,你就打開了處理網絡協議、圖像文件、壓縮數據等等的大門。
encoding/binary包 + 切片
如何使用encoding/binary進行字節序轉換?
字節序,也就是字節的排列順序,分為大端(Big Endian)和小端(Little Endian)。不同的系統或協議可能采用不同的字節序。encoding/binary包提供了BigEndian和LittleEndian兩個類型,分別代表大端和小端。
立即學習“go語言免費學習筆記(深入)”;
package main import ( "bytes" "encoding/binary" "fmt" ) func main() { var num int32 = 12345 // 示例數據 // 大端字節序 bufBig := new(bytes.Buffer) err := binary.Write(bufBig, binary.BigEndian, num) if err != nil { fmt.Println("binary.Write failed:", err) return } fmt.Printf("Big Endian: %Xn", bufBig.Bytes()) // 小端字節序 bufLittle := new(bytes.Buffer) err = binary.Write(bufLittle, binary.LittleEndian, num) if err != nil { fmt.Println("binary.Write failed:", err) return } fmt.Printf("Little Endian: %Xn", bufLittle.Bytes()) // 從字節數組讀取數據,并指定字節序 var readNum int32 reader := bytes.NewReader(bufLittle.Bytes()) err = binary.Read(reader, binary.LittleEndian, &readNum) if err != nil { fmt.Println("binary.Read failed:", err) return } fmt.Printf("Read Number: %dn", readNum) }
這個例子展示了如何將一個int32類型的數字,分別以大端和小端字節序寫入bytes.Buffer,并演示了如何從小端字節序的bytes.Buffer中讀取數據。注意,binary.Write和binary.Read的第二個參數就是用來指定字節序的。
如何處理變長二進制數據?
有時候,二進制數據的長度不是固定的,比如網絡協議中,經常會用一個字段來表示后續數據的長度。這時,就需要先讀取長度字段,再根據長度讀取實際數據。
package main import ( "bytes" "encoding/binary" "fmt" ) func main() { // 模擬一段變長數據,長度為10,數據為 "abcdefghij" data := []byte("abcdefghij") length := int32(len(data)) // 創建一個buffer,先寫入長度,再寫入數據 buf := new(bytes.Buffer) err := binary.Write(buf, binary.BigEndian, length) // 先寫入長度 if err != nil { fmt.Println("binary.Write length failed:", err) return } _, err = buf.Write(data) // 再寫入數據 if err != nil { fmt.Println("buf.Write data failed:", err) return } // 從buffer中讀取數據 readBuf := bytes.NewReader(buf.Bytes()) var readLength int32 err = binary.Read(readBuf, binary.BigEndian, &readLength) // 先讀取長度 if err != nil { fmt.Println("binary.Read length failed:", err) return } readData := make([]byte, readLength) _, err = readBuf.Read(readData) // 再讀取數據 if err != nil { fmt.Println("readBuf.Read data failed:", err) return } fmt.Printf("Read Length: %dn", readLength) fmt.Printf("Read Data: %sn", String(readData)) }
這段代碼模擬了變長數據的寫入和讀取。關鍵在于先讀取長度字段,然后根據長度字段創建切片,再讀取實際數據。
如何使用struct來定義二進制數據結構?
Golang的struct非常適合用來定義二進制數據結構。通過在struct字段上使用binary tag,可以指定字段的字節序和偏移量。
package main import ( "bytes" "encoding/binary" "fmt" ) // 定義一個結構體,模擬二進制數據結構 type MyData struct { ID int32 // ID Name [16]byte // Name, 固定長度16字節 Value float64 // Value } func main() { // 創建一個MyData實例 myData := MyData{ ID: 123, Name: [16]byte{'G', 'o', 'l', 'a', 'n', 'g'}, // 初始化部分Name Value: 3.1415926, } // 將結構體寫入buffer buf := new(bytes.Buffer) err := binary.Write(buf, binary.LittleEndian, &myData) if err != nil { fmt.Println("binary.Write failed:", err) return } // 從buffer中讀取數據,并填充到結構體 readBuf := bytes.NewReader(buf.Bytes()) var readData MyData err = binary.Read(readBuf, binary.LittleEndian, &readData) if err != nil { fmt.Println("binary.Read failed:", err) return } // 打印讀取到的數據 fmt.Printf("ID: %dn", readData.ID) fmt.Printf("Name: %sn", string(readData.Name[:])) // 注意截斷,去掉多余的空字節 fmt.Printf("Value: %fn", readData.Value) }
在這個例子中,MyData結構體定義了一個包含ID、Name和Value字段的二進制數據結構。使用binary.Write和binary.Read可以直接將結構體寫入和讀取到bytes.Buffer。注意,對于固定長度的字符串,需要使用[N]byte數組,而不是string類型。另外,讀取Name字段時,需要進行截斷,去掉多余的空字節。