encoding 编解码

Go语言的encoding包中提供了很多常见的编解码、序列化和反序列化功能,比如二进制、Base64、JSON、XML等,这篇笔记我们简单介绍这些编码工具的用法。

encoding/base64 Base64编解码

encoding/base64包提供了base64的编解码工具,下面是一些例子代码。

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {
    str := "Hello, world!"
    // BASE64编码
    base64Str := base64.StdEncoding.EncodeToString([]byte(str))
    // BASE64解码
    bytes, err := base64.StdEncoding.DecodeString(base64Str)
    if err != nil {
        fmt.Println(err)
        return
    }
    srcStr := string(bytes)

    fmt.Println(base64Str)
    fmt.Println(srcStr)
}

代码中,我们对字符串s调用了EncodeToString()方法将其编码为BASE64字符串,然后又用DecodeString()方法将其解码为[]byte数组,最后转换为原字符串。

encoding/json JSON序列化和反序列化

encoding/json包能够实现struct结构体或是map与JSON字符串之间相互转换。其中主要包含两个方法:

Marshal():序列化为JSON字符串

Unmarshal():反序列化JSON

struct序列化为JSON

下面代码是将结构体序列化为JSON的例子。

package main

import (
    "encoding/json"
    "fmt"
)

type RespJson struct {
    Status int    `json:"status"`
    Msg    string `json:"msg"`
}

func main() {
    resp := RespJson{200, "success"}
    bytes, err := json.Marshal(&resp)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(bytes))
}

上面代码中在定义结构体时,类型后面还有一个反引号包裹的字符串,这被称为tag语法,我们通过Tag标签的形式指定了序列化为JSON时该字段的字段名。

JSON反序列化为struct

下面代码中,我们将一个JSON字符串反序列化为了一个结构体对象。

package main

import (
    "encoding/json"
    "fmt"
)

type RespJson struct {
    Status int    `json:"status"`
    Msg    string `json:"msg"`
}

func main() {
    str := "{\"status\":200,\"msg\":\"success\"}"
    resp := RespJson{}
    if err := json.Unmarshal([]byte(str), &resp); err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(resp)
}

map序列化为JSON

除了结构体,Marshal()Unmarshal()方法也支持map,下面例子我们将一个map序列化为了JSON对象。

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    m := make(map[string]interface{})
    m["status"] = 200
    m["msg"] = "success"
    bytes, err := json.Marshal(m)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(bytes))
}

和其它语言一样,我们应该尽量避免使用map作为JSON数据对应的存储形式,大量的map会降低代码的可读性,不仅更容易出错而且会导致代码后续难以维护和修改。

encoding/binary 二进制读写

encoding/binary包提供了将基本数据类型与字节序列之间进行转换的功能。这个包的主要用途是对数据进行二进制编码和解码操作,特别适合在网络通信、二进制文件读写等需要处理原始二进制数据的场景中使用。

下面例子我们使用encoding/binary读写uint32和一个字符串的二进制数组。

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
)

func main() {
    // 创建动态缓冲区
    var buf = bytes.Buffer{}
    // 要写入的数据
    var value uint32 = 13
    str := "Hello, world!"
    // 写入数据
    if err := binary.Write(&buf, binary.BigEndian, value); err != nil {
        fmt.Println(err)
        return
    }
    if err := binary.Write(&buf, binary.BigEndian, []byte(str)); err != nil {
        fmt.Println(err)
        return
    }
    // 读取数据
    strBytes := make([]byte, 13)
    if err := binary.Read(&buf, binary.BigEndian, &value); err != nil {
        fmt.Println(err)
        return
    }
    if err := binary.Read(&buf, binary.BigEndian, &strBytes); err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(value)
    fmt.Println(string(strBytes))
}

encoding/binary包提供了binary.Readbinary.Write函数用于读取和写入二进制数据,这两个函数的第1个参数是实现了io.Readerio.Writer接口的对象,它可以是文件、网络套接字或是类似上面代码中使用的动态长度内存缓冲区bytes.Buffer;第2个参数是字节序,其中binary.BigEndian表示大端字节序,binary.LittleEndian表示小端字节序;第3个参数就是具体要读取或写入的变量。

注意:我们要知道encoding/binary只能处理定长数据,对于变长数据(如字符串、切片、变长数组等),它不会直接处理,上面代码中我们处理字符串时也是将其转换为了[]byte类型。

除了基本类型,我们也可以使用结构体封装一组数据供encoding/binary包读写,但和之前类似,结构体内部的字段也必须是定长的,下面是一个例子。

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
)

type Point struct {
    X int32
    Y int32
}

func main() {
    // 创建动态缓冲区
    var buf = bytes.Buffer{}
    // 要写入的数据
    point := Point{X: -3, Y: 15}
    // 写入数据
    if err := binary.Write(&buf, binary.BigEndian, &point); err != nil {
        fmt.Println(err)
        return
    }
    // 读取数据
    result := Point{}
    if err := binary.Read(&buf, binary.BigEndian, &result); err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(result)
}
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。