儲存到CSV檔案中
本文接上一篇:記憶體儲存。
關於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}