本篇文章仍然接前面的文章:内存存储,主要介绍将博客文章数据序列化持久到文件中。html
encoding/gob包用于编码器和解码器之间进行二进制流的数据交换,例如发送端和接收端之间交换数据。也能用来实现对象序列化,并持久保存的功能,而后再须要的时候加载并解码成原始数据。golang
gob包的用法很简单,但有必要去了解它的背景知识,见官方手册:https://golang.org/pkg/encoding/gob/数据结构
下面是序列化到文件中的函数代码:app
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)进行文件建立。post
另外,gob不会序列化指针,它会找到指针所指向的数据对象,并对数据对象进行序列化。编码
由于这个函数接受任意类型的数据结构,因此能够将map类型的PostById和PostByAuthor进行序列化,也能够将Post类型的单篇文章进行序列化。指针
storeToGob(PostById, "d:/PostById.gob") storeToGob(PostByAuthor, "d:/PostByAuthor.gob") storeToGob(post3, "d:/post3.gob")
序列化以后,能够加载序列化后的文件进行解码。加载gob文件并解码二进制数据的函数以下:code
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中。htm
惟一须要注意的是解码器的解码方法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) } }