返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

encoding 1.18

发布于 2024-10-12 19:15:51 字数 7566 浏览 0 评论 0 收藏 0

常用编码转换器。

  • base64 :基于 64 个可打印字符来表示二进制数据。
  • binary :转换数字和字节数组。(byte order)
  • csv :读写 CSV 表格文件。
  • gob :二进制序列化。
  • hex :十六进制编码。
  • json :JSON 编码。
  • pem :TLS 密钥和证书编码。(参考 crypto/rsa
  • xml :XML 解析器。

base64

使用 NewEncoder ,记得调用 Close

package main

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"log"
	"fmt"
)

func main() {
	d := make([]byte, 10)
	rand.Read(d)

	s := base64.StdEncoding.EncodeToString(d)
	b, _ := base64.StdEncoding.DecodeString(s)

	if !bytes.Equal(d, b) { log.Fatal() }
	fmt.Println(s)
}

// oLzIfNdzFRF7hA==
package main

import (
	"bytes"
	"encoding/base64"
)

func main() {
	b := bytes.NewBuffer(nil)

	w := base64.NewEncoder(base64.StdEncoding, b)
	w.Write([]byte("1234"))
	w.Write([]byte("abc"))
	w.Close()                   // !!!!

	println(b.String())
    
    // ------------------------------------------

	r := base64.NewDecoder(base64.StdEncoding, b)
	bs := make([]byte, base64.StdEncoding.DecodedLen(b.Len()))
	r.Read(bs)

	println(string(bs))
}

// MTIzNGFiYw==
// 1234abc

binary

字节存储顺序(byte order)和处理器架构有关,如 Intel x86 是小端序。

  • 大端(big endian):高位字节存放在低端地址。
  • 小端(little endian):低位字节存放在低端地址。

注意,相关函数是否要求固定长度(fixed)类型。(如 int64 ,非 int

package main

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

func main() {
	var x int64 = 0x11223344

	big := bytes.NewBuffer(nil)
	binary.Write(big, binary.BigEndian, x)

	little := bytes.NewBuffer(nil)
	binary.Write(little, binary.LittleEndian, x)

	fmt.Printf("B: % x\n", big.Bytes())
	fmt.Printf("L: % x\n", little.Bytes())
}

// B: 00 00 00 00 11 22 33 44
// L: 44 33 22 11 00 00 00 00

判断当前系统大小端。

func main() {
	
    // 小端: [01, 00 ...]
    x := 1
    
	p := (*byte)(unsafe.Pointer(&x))
	println(*p == 1)  
}

csv

逗号分隔值(Comma-Separated Values),以纯文本格式存储表格数据(数字和文本)。

  • 纯文本,使用某个字符集。比如 ASCII、Unicode、EBCDIC 或 GB2312。
  • 由记录组成(典型的是每行一条记录)。
  • 每条记录被分隔符分隔为字段(典型分隔符有逗号、分号或制表符)。
  • 每条记录都有同样的字段序列。

双引号

用双引号( " )表示包含空格的字段内容,其中可包含换行和分隔符。
两个连续双引号进行转义。

空白

空格等属于字段内容的组成部分。
空行被忽略,但由空白符填充的行不是空行。
字段可以没有内容,但记录必须有同等数量分隔符。
行末的换行回车符会被删除。

package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"log"
	"strings"
)

func main() {
	b := bytes.NewBuffer(nil)

	w := csv.NewWriter(b)
	w.Write([]string{"user1", "18", `data: "example"`})
	w.Write([]string{"user2", "20", `data: "test xxx"`})
	w.Flush()

	fmt.Println(b)

    // ---------------------------------------------
    
	r := csv.NewReader(b)
	recs, err := r.ReadAll()
	if err != nil { log.Fatalln(err) }

	for _, rec := range recs {
		fmt.Println(strings.Join(rec, " | "))
	}
}

/*

user1,18,"data: ""example"""
user2,20,"data: ""test xxx"""

user1 | 18 | data: "example"
user2 | 20 | data: "test xxx"

*/

gob

自带二进制序列化方案。

  • 性能好过 encoding/json ,但不如 msgpackprotocol buffer
  • 可用来实现深拷贝(deep copy)。
  • 因内部使用 reflect 实现,所以只处理导出字段。
  • 编解码双方类型结构不要求完全一致,包括不同顺序。
  • 忽略不匹配的字段,但最少有一个名字相同的字段。
  • 支持值和指针转换,深度递归字段值。
package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"log"
)

func main() {
	a := struct {
		X int
		Y string
		z int16   // ignore
	}{
		100,
		"abc",
		12,
	}

	b := struct {
		B bool
		Y *string
		X *int32
		Z int16
	}{}

	// -----------------------------------

	data := bytes.NewBuffer(nil)

	enc := gob.NewEncoder(data)
	if err := enc.Encode(&a); err != nil { log.Fatalln(err) }

	fmt.Printf("% x\n", data.Bytes())

    // -----------------------------------
    
	dec := gob.NewDecoder(data)
	if err := dec.Decode(&b); err != nil { log.Fatalln(err) }

	fmt.Printf("%+v\n", b)
	fmt.Println(*b.X, *b.Y)
}

/*

18 ff 81 03 01 02 ff 82 00 ... 62 63 00

{B:false Y:0xc00008b590 X:0xc0000ae3b8 Z:0}
100 abc

*/

hex

十六进制字符串转换,中间不能有空格。

package main

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"log"
)

func main() {
	b := []byte{0x11, 0x22, 0x33}

	s := hex.EncodeToString(b)
	b2, err := hex.DecodeString(s)

	if err != nil { log.Fatalln(err) }
	if !bytes.Equal(b, b2) { log.Fatal() }

	fmt.Printf("%s\n% x\n", s, b2)
}

// 112233
// 11 22 33

比较有意思的是 Dump ,可以按习惯的十六进制编辑器方式输出。

func main() {
	b := []byte("hello, world!")
	fmt.Println(hex.Dump(b))
}

/*

00000000  68 65 6c 6c 6f 2c 20 77  6f 72 6c 64 21    |hello, world!|

*/

json

JSON 格式序列化转换。

  • 递归对象字段结构,须是导出成员。
  • 自动调用 MarshalJSONMarshalText 接口方法。
  • 支持指针。
package main

import (
	"bytes"
	"encoding/json"
	"log"
)

func main() {
	x := 100

	d := struct {
		X *int
		S string
		Y []int
	}{
		X: &x,
		S: "hello, world!",
		Y: []int{1, 2, 3, 4},
	}

	// -----------------------

	b, err := json.Marshal(d)
	if err != nil { log.Fatalln(err) }

	println(string(b))

	// -----------------------

	buf := bytes.NewBuffer(nil)
	json.Indent(buf, b, "", "    ")

	println(buf.String())
}

/*

{"X":100,"S":"hello, world!","Y":[1,2,3,4]}

{
    "X": 100,
    "S": "hello, world!",
    "Y": [
        1,
        2,
        3,
        4
    ]
}

*/

标记

struct tag 标记 JSON 格式。

json:"[name|-],[omitempty|string]"
  • - :忽略。
  • omitempty :空值时忽略。
package main

import (
	"bytes"
	"encoding/json"
	"log"
)

func main() {
	x := 100

	d := struct {
		X *int   `json:"id"`
		S string `json:"name"`
		Y []byte `json:"data,omitempty"`
	}{
		X: &x,
		S: "hello, <world!>",
	}

	// -----------------------

	b, err := json.Marshal(d)
	if err != nil { log.Fatalln(err) }

	// -----------------------

	buf := bytes.NewBuffer(nil)
	json.Indent(buf, b, "", "    ")

	println(buf.String())
}

/*

{
    "id": 100,
    "name": "hello, \u003cworld!\u003e"
}

*/

解码目标除结构体外,也可以是 map (无需创建)。

  • 忽略不存在的字段。
  • 优先精确匹配字段名。找不到时,忽略大小写。
  • 字段顺序不影响。
  • 可为匿名字段添加 tag 标记。
  • 字段名和匿名字段冲突,当前字段优先。
  • 相同层次的多个匿名字段冲突,忽略冲突字段。
package main

import (
	"encoding/json"
	"log"
	"fmt"
)

type U struct {
	Id   int
	Name string
}

type M struct {
	U
	Title string
}

func main() {
    
	d := M { U{1, "user1"}, "cxo" }

	// ----------------------------

	b, err := json.Marshal(d)
	if err != nil { log.Fatalln(err) }

	println(string(b))

	// ----------------------------

    // 相同类型。

    func() {
		var x M

		if err := json.Unmarshal(b, &x); err != nil {
			log.Fatalln(err)
		}

		fmt.Printf("%+v\n", x)
	}()

	// ----------------------------

    // 不同类型。
    // ID 必须是导出成员,后续字母大小写可被忽略。
    
    func() {
		var x struct {
			ID    int
			Title string
		}

		if err := json.Unmarshal(b, &x); err != nil {
			log.Fatalln(err)
		}

		fmt.Printf("%+v\n", x)
	}()

	// ----------------------------

    // 字典。
    
    func() {
		var m map[string]interface{}

		if err := json.Unmarshal(b, &m); err != nil {
			log.Fatalln(err)
		}

		fmt.Printf("%v\n", m)
	}()
}

/*

{"Id":1,"Name":"user1","Title":"cxo"}

{U:{Id:1 Name:user1} Title:cxo}
{ID:1 Title:cxo}
map[Id:1 Name:user1 Title:cxo]

*/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文