Go 学习笔记(三十七)数据存储 (2)——CSV 文件
本文原创地址:博客园骏马金龙Go Web:数据存储 (2)——CSV 文件
存储到 CSV 文件中
当数据存储到了内存中,可以在需要的时候持久化保存到磁盘文件中。比如保存为 csv 格式的文件,下一篇再介绍序列化持久到文件中。
下面是持久化到 csv 文件的函数代码:
func storeToCsv(filename string, posts map[int]*Post) {
// 创建文件
csvFile, err := os.Create(filename)
if err != nil {
panic(err)
}
defer csvFile.Close()
// 获取csv的Writer
writer := csv.NewWriter(csvFile)
// 将map中的Post转换成slice,因为csv的Write需要slice参数
// 并写入csv文件
for _, post := range posts {
record := []string{strconv.Itoa(post.Id), post.Content, post.Author}
err1 := writer.Write(record)
if err1 != nil {
panic(err1)
}
}
// 确保所有内存数据刷到csv文件
writer.Flush()
}
这个函数的逻辑很简单,无需过多的解释。需要注意的是,os.Create()
函数在文件存在时会截断文件,如有需要,可以考虑使用追加写入的相关函数。
当需要将保存在内存中的 post 保存到 csv 文件时,只需调用该函数,传递一个文件名以及 PostById 作为参数即可:
storeToCsv("d:/a.csv", PostById)
保存之后,以下是 a.csv 文件的内容:
2,Hello 2,userB
3,Hello 3,userC
4,Hello 4,userA
1,Hello 1,userA
数据保存到了 csv 文件,自然需要从 csv 文件中读取数据到内存。以下是读取 csv 文件的函数:
func load(filename string) []*Post {
// 打开文件
file, err := os.Open(filename)
if err != nil {
panic(err)
}
defer file.Close()
// 获取csv的reader
reader := csv.NewReader(file)
// 设置FieldsPerRecord为-1
reader.FieldsPerRecord = -1
// 读取文件中所有行保存到slice中
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
var posts []*Post
// 将每一行数据保存到内存slice中
for _, item := range records {
id, _ := strconv.Atoi(item[0])
post := &Post{Id: id, Content: item[1], Author: item[2]}
posts = append(posts, post)
}
return posts
}
逻辑也很简单,唯一需要注意的是FiledsPerRecord=-1
:
- 设置为负数表示读取时每条记录的字段数量可以随意
- 如果设置为正数 N,则表示每条记录必须且只读取 N 个字段,如果字段少于 N,则报错
- 如果设置为 0,则表示按照第一条记录所拥有的字段数量进行读取剩余记录,也就是说每一条记录的字段数量都必须和第一条记录相同
这个 load() 函数返回一个 slice,这个 slice 中保存了所有读取到的文章指针。
s := load("d:/a.csv")
因为使用var PostById map[int]*Post
和var PostsByAuthor map[string][]*Post
保存一篇篇的文章,迭代此 slice 即可将 slice 中的 post 保存到这两个 map 中。
for _, post := range s {
store(post)
}
然后就可以从这两个 map 中按照 Id 或者按照 Author 进行检索:
fmt.Println(PostById[1])
fmt.Println(PostById[2])
for _, post := range PostsByAuthor["userA"] {
fmt.Println(post)
}
下面是完整的保存到 csv 文件以及读取 csv 文件的代码:
package main
import (
"encoding/csv"
"fmt"
"os"
"strconv"
)
type Post struct {
Id int
Content string
Author string
}
var PostById map[int]*Post
var PostsByAuthor map[string][]*Post
func store(post *Post) {
PostById[post.Id] = post
PostsByAuthor[post.Author] = append(PostsByAuthor[post.Author], post)
}
func storeToCsv(filename string, posts map[int]*Post) {
csvFile, err := os.Create(filename)
if err != nil {
panic(err)
}
defer csvFile.Close()
writer := csv.NewWriter(csvFile)
for _, post := range posts {
record := []string{strconv.Itoa(post.Id), post.Content, post.Author}
err1 := writer.Write(record)
if err1 != nil {
panic(err1)
}
}
writer.Flush()
}
func load(filename string) []*Post {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
defer file.Close()
reader := csv.NewReader(file)
reader.FieldsPerRecord = -1
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
var posts []*Post
for _, item := range records {
id, _ := strconv.Atoi(item[0])
post := &Post{Id: id, Content: item[1], Author: item[2]}
posts = append(posts, post)
}
return posts
}
func main() {
PostById = make(map[int]*Post)
PostsByAuthor = 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)
// 将内存中的map容器,保存到csv文件中
storeToCsv("d:/a.csv", PostById)
// 为了测试,此处将已保存在内存中的数据清空
PostById = map[int]*Post{}
PostsByAuthor = map[string][]*Post{}
// 下面是加载csv文件
s := load("d:/a.csv")
for _, post := range s {
store(post)
}
// 检索
fmt.Println(PostById[1])
fmt.Println(PostById[2])
for _, post := range PostsByAuthor["userA"] {
fmt.Println(post)
}
for _, post := range PostsByAuthor["userC"] {
fmt.Println(post)
}
}
运行结果:
&{1 Hello 1 userA}
&{2 Hello 2 userB}
&{1 Hello 1 userA}
&{4 Hello 4 userA}
&{3 Hello 3 userC}
上一篇 Go 学习笔记(三十六)数据存储 (1)——内存存储
Go 学习笔记(目录)
下一篇 Go 学习笔记(三十八)数据存储 (3)——gob 对象序列化