• Go Web:数据存储(2)——CSV文件


    存储到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]*Postvar 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}
    
  • 相关阅读:
    GitLab 重置认证和添加账号缓存
    PHP 正则匹配IP
    git 删除指定版本
    PostgreSQL 9.2 日期运算
    postgre 已有字段修改为自增
    postgresql 导入导出
    PHP TS 和 NTS 版本选择
    background-image属性的设置
    SQLServer 附加数据库后只读或报错解决方法
    IIS 6.0 发布网站使用教程
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/10060242.html
Copyright © 2020-2023  润新知