• Go Web:数据存储(3)——gob对象序列化


    序列化持久存储gob

    本篇文章仍然接前面的文章:内存存储,主要介绍将博客文章数据序列化持久到文件中。

    encoding/gob包用于编码器和解码器之间进行二进制流的数据交换,例如发送端和接收端之间交换数据。也能用来实现对象序列化,并持久保存的功能,然后再需要的时候加载并解码成原始数据。

    gob包的用法很简单,但有必要去了解它的背景知识,见官方手册:https://golang.org/pkg/encoding/gob/

    下面是序列化到文件中的函数代码:

    func storeToGob(data interface{}, filename string) {
    	buffer := new(bytes.Buffer)
    	encoder := gob.NewEncoder(buffer)
    	err := encoder.Encode(data)
    	if err != nil {
    		panic(err)
    	}
    	err = ioutil.WriteFile(filename, buffer.Bytes(), 0600)
    	if err != nil {
    		panic(err)
    	}
    }
    

    其中gob.NewEncoder()函数用来生成一个gob的流编码器。通过编码器的Encode()方法,可以将数据编码成二进制格式,Encode()方法的参数类型是interface{},所以storeToGob()函数的第一个参数也指定为此类型可以将任意数据结构序列化。然后只需将序列化到buffer中的数据写入到文件中即可。

    需要注意的是上面使用ioutil.WriteFile()函数会在文件存在时进行截断,文件不存在时以给定权限(上面给的权限是600)进行文件创建。

    另外,gob不会序列化指针,它会找到指针所指向的数据对象,并对数据对象进行序列化

    因为这个函数接受任意类型的数据结构,所以可以将map类型的PostById和PostByAuthor进行序列化,也可以将Post类型的单篇文章进行序列化。

    storeToGob(PostById, "d:/PostById.gob")
    storeToGob(PostByAuthor, "d:/PostByAuthor.gob")
    storeToGob(post3, "d:/post3.gob")
    

    序列化之后,可以加载序列化后的文件进行解码。加载gob文件并解码二进制数据的函数如下:

    func load(data interface{}, filename string) {
    	raw, err := ioutil.ReadFile(filename)
    	if err != nil {
    		panic(err)
    	}
    	buffer := bytes.NewBuffer(raw)
    	dec := gob.NewDecoder(buffer)
    	err = dec.Decode(data)
    	if err != nil {
    		panic(err)
    	}
    }
    

    逻辑很简单,从文件中读取数据,并解码后保存到data中。

    唯一需要注意的是解码器的解码方法Decode()的参数虽然是interface{}类型的,但却要求只能是指针类型。如果传参时传递的是非指针类型,将报错。

    例如,分别解码前文保存的3个gob文件,并分别保存到对应的数据结构中:

    load(&PostById, "d:/PostById.gob")
    fmt.Println(PostById[1])
    fmt.Println(PostById[2])
    
    load(&PostByAuthor, "d:/PostByAuthor.gob")
    for _, post := range PostByAuthor["userA"] {
    	fmt.Println(post)
    }
    
    var post33 *Post
    load(&post33, "d:/post3.gob")
    fmt.Println(post33)
    

    注意上面调用load()函数时,传递的第一个参数都是指针类型的。post33变量自身就是指针,所以上面load(post33,"d:/post3.gob")也是可行的,但传递post33&post33的结果是不一样的,如下:

    // load(post33, "d:/post3.gob")
    {3 Hello 3 userC}
    
    // load(&post33, "d:/post3.gob")
    &{3 Hello 3 userC}
    

    现在数据又回到了内存存储结构上,可以进行正常的增、删、改、查等操作。

    下面是完整的代码:

    package main
    
    import (
    	"bytes"
    	"encoding/gob"
    	"fmt"
    	"io/ioutil"
    )
    
    type Post struct {
    	Id      int
    	Content string
    	Author  string
    }
    
    var PostById map[int]*Post
    var PostByAuthor map[string][]*Post
    
    func store(post *Post) {
    	PostById[post.Id] = post
    	PostByAuthor[post.Author] = append(PostByAuthor[post.Author], post)
    }
    
    func storeToGob(data interface{}, filename string) {
    	buffer := new(bytes.Buffer)
    	encoder := gob.NewEncoder(buffer)
    	err := encoder.Encode(data)
    	if err != nil {
    		panic(err)
    	}
    	err = ioutil.WriteFile(filename, buffer.Bytes(), 0600)
    	if err != nil {
    		panic(err)
    	}
    }
    
    func load(data interface{}, filename string) {
    	raw, err := ioutil.ReadFile(filename)
    	if err != nil {
    		panic(err)
    	}
    	buffer := bytes.NewBuffer(raw)
    	dec := gob.NewDecoder(buffer)
    	err = dec.Decode(data)
    	if err != nil {
    		panic(err)
    	}
    }
    
    func main() {
    	PostById = make(map[int]*Post)
    	PostByAuthor = make(map[string][]*Post)
    	post1 := &Post{Id: 1, Content: "Hello 1", Author: "userA"}
    	post2 := &Post{Id: 2, Content: "Hello 2", Author: "userB"}
    	post3 := &Post{Id: 3, Content: "Hello 3", Author: "userC"}
    	post4 := &Post{Id: 4, Content: "Hello 4", Author: "userA"}
    	store(post1)
    	store(post2)
    	store(post3)
    	store(post4)
    
    	storeToGob(PostById, "d:/PostById.gob")
    	storeToGob(PostByAuthor, "d:/PostByAuthor.gob")
    	storeToGob(post3, "d:/post3.gob")
    
    	PostById = map[int]*Post{}
    	PostByAuthor = map[string][]*Post{}
    
    	var post33 *Post
    	load(&post33, "d:/post3.gob")
    	fmt.Println(post33)
    
    	load(&PostById, "d:/PostById.gob")
    	fmt.Println(PostById[1])
    	fmt.Println(PostById[2])
    
    	load(&PostByAuthor, "d:/PostByAuthor.gob")
    	for _, post := range PostByAuthor["userA"] {
    		fmt.Println(post)
    	}
    }
    
  • 相关阅读:
    Laravel kalnoy/nestedset
    Eloquent Subquery Enhancements in Laravel 6.0
    Laravel 6.0 中更加强劲的子查询
    Es6系列之module and class
    Using Laravel's Bootable Eloquent Traits
    Google Chrome Shortcut Keys
    另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新
    docker-machine 创建主机的缺省密码 (Default User and Password)
    eslint Cannot read property 'range' of null错误( Quasar Doc )$ quasar dev 出错
    java比较排序Comparable和Comparator
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/10060245.html
Copyright © 2020-2023  润新知