golang如何操作csv文件详解

目录
  • 练习要求:
  • 考察点:
  • 编码:
  • README.md
  • 附:使用Golang导出CSV数据并解决数据乱码问题
    • CSV 格式
    • 实现方式
    • golang实现csv数据写文件
    • golang实现web导出csv数据
  • 总结

练习要求:

写一个小程序解析data.csv,要求实现如下功能:

  1. 接收姓名作为参数。
  2. 根据姓名查找出对应员工的工时信息,并将 日期、上班、下班、工时 打印到标准输出。
  3. 将上一条输出的内容保存到json文件,使用姓名.json作为文件名
  4. 根据上条中生成的json文件,计算出该员工的月总工时、每周的平均工时。

考察点:

  1. 结构体定义
  2. 字符串拼接
  3. 类型转换
  4. 编码转换
  5. 命令行参数解析
  6. 文件读取
  7. json库使用

编码:

package main

import (
    "bufio"
    "encoding/json"
    "errors"
    "flag"
    "fmt"
    "io"
    "os"
    "strconv"
    "strings"

    "github.com/axgle/mahonia"
)

//给 fmt.Println 起一个短的别名。
var p = fmt.Println

//定义一个全局变量 一个月上班加休息总天数
var gAllDays float64 = 0

//定义一个全局变量 考勤异常的天数
var gAbnormalDays int = 0

//上班信息
type WorkInfo struct {
    WorkDate  string //上班日期
    StartTime string //上班打卡时间
    EndTime   string //下班打卡时间
    LaborHour string //当天工时
}

//考勤异常信息
type WorkAbnormalInfo struct {
    WorkDate   string //上班日期
    NormalInfo string //异常信息
}

/**
 * @brief  把当前字符串按照指定方式进行编码
 * @param[in]       src                   待进行转码的字符串
 * @param[in]       srcCode               字符串当前编码
 * @param[in]       tagCode               要转换的编码
 * @return   进行转换后的字符串
 */
func ConvertToString(src string, srcCode string, tagCode string) (string, error) {
    if len(src) == 0 || len(srcCode) == 0 || len(tagCode) == 0 {
        return "", errors.New("input arguments error")
    }
    srcCoder := mahonia.NewDecoder(srcCode)
    srcResult := srcCoder.ConvertString(src)
    tagCoder := mahonia.NewDecoder(tagCode)
    _, cdata, _ := tagCoder.Translate([]byte(srcResult), true)
    result := string(cdata)

    return result, nil
}

/**
 * @brief            写入数据到指定名字的文件中
 * @param[in]       buf                    待写入的数据内容
 * @param[in]       name                文件名字
 * @return   成功返回nil 失败返回error    错误信息
 */
func WriteFile(name string, buf string) error {
    if len(name) == 0 || len(buf) == 0 {
        return errors.New("input arguments error")
    }

    fout, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0666)
    defer fout.Close()
    if err != nil {
        return err
    }

    //写入到本地文件中
    fout.WriteString(buf)

    return nil
}

/**
 * @brief  读取文件
 * @param[in]       name            文件名(可以加路径)
 * @return   成功返回 文件内容,失败返回error    错误信息
 */
func ReadFile(name string) ([]byte, error) {
    if len(name) == 0 {
        return nil, errors.New("input arguments error")
    }

    //打开本地文件 读取出全部数据
    fin, err := os.Open(name)
    defer fin.Close()
    if err != nil {
        return nil, errors.New("Close error")
    }

    buf_len, _ := fin.Seek(0, os.SEEK_END)
    fin.Seek(0, os.SEEK_SET)

    buf := make([]byte, buf_len)
    fin.Read(buf)

    return buf, nil
}

/**
 * @brief  读取csv文件并打印指定员工信息
 * @param[in]       csvName            csv文件名(可以加路径)
 * @param[in]       employeeName        员工名字
 * @return   成功返回 员工结构体信息,失败返回error    错误信息
 */
func ReadCsvFile(csvName string, employeeName string) ([]WorkInfo, error) {
    if len(csvName) == 0 || len(employeeName) == 0 {
        return nil, errors.New("error: input arguments error")
    }

    var WorkInfoSet []WorkInfo
    var AbnormalSet []WorkAbnormalInfo
    var isExistName bool
    var dayCount float64 = 0
    var isNormal string
    var isNormalFlag bool

    var index int = 0
    var indexWorkDate int
    var indexStartTime int
    var indexEndTime int
    var indexLaborHour int
    var indexNormalInfo int
    var indexIsNormal int

    var i int = 0

    f, err := os.Open(csvName)
    if err != nil {
        return nil, err
    }
    defer f.Close()

    rd := bufio.NewReader(f)
    for {
        gbk_line, err := rd.ReadString('\n') //以'\n'为结束符读入一行
        if err != nil || io.EOF == err {
            break
        }
        //p("gbk:", gbk_line)

        //把每一行gbk格式的字符串 转换为 utf-8格式字符串
        utf8_line, _ := ConvertToString(gbk_line, "gbk", "utf-8")

        //对第一行进行处理
        if i == 0 {
            i = 1 //保证 只有第一行被处理
            p("utf8:", utf8_line)
            first_line := strings.Split(utf8_line, ",")

            for _, val := range first_line {

                if val == "日期" {
                    indexWorkDate = index
                }
                if val == "上班" {
                    indexStartTime = index
                }
                if val == "下班" {
                    indexEndTime = index
                }
                if val == "工时" {
                    indexLaborHour = index
                }
                if val == "是否有考勤异常" {
                    indexIsNormal = index
                }
                if val == "工时异常" {
                    indexNormalInfo = index
                }

                index++
            }
        }

        if strings.Contains(utf8_line, employeeName) {
            //把存在员工标记为true
            isExistName = true

            split_line := strings.Split(utf8_line, ",")
            person_temp := WorkInfo{split_line[indexWorkDate],
                split_line[indexStartTime],
                split_line[indexEndTime],
                split_line[indexLaborHour],
            }

            //考勤表天数加1
            dayCount++
            isNormal = split_line[indexIsNormal]
            //统计打卡异常的信息
            if isNormal == "是" {
                aInfo := WorkAbnormalInfo{split_line[indexWorkDate], split_line[indexNormalInfo]}
                AbnormalSet = append(AbnormalSet, aInfo)

                gAbnormalDays++
                isNormalFlag = true
            }

            WorkInfoSet = append(WorkInfoSet, person_temp)
        }
    }

    //统计考勤表里所有天数
    gAllDays = dayCount
    //对于不存在指定员工名字 的处理
    if !isExistName {
        p("\nRemind: There is no employee is csv file!\n")
        os.Exit(1)
    }

    //显示员工所有考勤信息
    p("\n员工姓名:", employeeName)
    p("\n全部考勤信息:")
    for _, temp := range WorkInfoSet {
        fmt.Printf(
            "日期:%s ,上班:%s,下班:%s,工时:%s\n",
            temp.WorkDate,
            temp.StartTime,
            temp.EndTime,
            temp.LaborHour,
        )
    }

    //显示员工打卡异常信息
    if isNormalFlag {
        p("\n异常考勤信息:")
        for _, val := range AbnormalSet {
            fmt.Printf("日期:%s , 异常信息:%s\n", val.WorkDate, val.NormalInfo)
        }
        p("温馨提示:考勤出现异常信息,请及时给助理说明情况~_~\n")
    }

    return WorkInfoSet, nil
}

/**
* @brief  写入json文件
* @param[in]       employeeName        员工名字
* @param[in]       workInfoSet            员工结构体信息
* @return   成功返回 nil,失败返回error    错误信息
 */
func WriteJsonFile(employeeName string, workInfoSet []WorkInfo) error {
    if len(employeeName) == 0 || workInfoSet == nil {
        return errors.New("error: input arguments error")
    }

    //把输出内容写入name.json文件中
    filename := fmt.Sprintf("%s%s", employeeName, ".json")
    str, _ := json.Marshal(workInfoSet)
    err := WriteFile(string(str), filename)
    if err != nil {
        return err
    }
    return nil
}

/**
* @brief  读取json文件
* @param[in]       employeeName        员工名字
* @return   成功返回 nil,失败返回error    错误信息
 */
func ReadJsonFile(employeeName string) error {
    if len(employeeName) == 0 {
        return errors.New("error: input arguments error")
    }

    var WorkInfoSet []WorkInfo
    filename := fmt.Sprintf("%s%s", employeeName, ".json")

    ReadJsonBuf, err := ReadFile(filename)
    if err != nil {
        p(err.Error())
        return err
    }
    var sumHour float64 = 0.0
    var dayCount float64 = 0
    var weekCounts float64 = 0.0
    var averageWeekHour float64 = 0.0

    json.Unmarshal(ReadJsonBuf, &WorkInfoSet)

    for _, one_work := range WorkInfoSet {

        //去掉打卡异常情况和周六末情况 (如果周六末加班 数据依然计算进入总工时)
        if one_work.StartTime == "" || one_work.EndTime == "" {
            continue
        }

        one_day_hour, _ := strconv.ParseFloat(one_work.LaborHour, 64)
        sumHour += one_day_hour
        dayCount++
    }

    fmt.Printf("根据json文件计算工时,考勤正常天数:%2.0f, 异常天数:%d\n", dayCount, gAbnormalDays)
    weekCounts = gAllDays / 7
    averageWeekHour = sumHour / weekCounts
    //p("考勤表总天数:", gAllDays, ",共多少周:", week_counts)

    fmt.Printf("月总工时:%.4f 每周的平均工时:%.4f\n\n", sumHour, averageWeekHour)

    return nil
}

func main() {
    args := os.Args

    input := flag.String("i", "查无此人", "input employee name")
    path := flag.String("p", "./data.csv", "input csv file path")

    flag.Parse()

    if len(args) == 1 {
        fmt.Println("./main: missing operand")
        fmt.Println("Try `./main -h' or './main --help' for more information.")
        return
    }

    var csvName string = *path
    var employeeName string = *input

    //读取csv文件并打印指定员工信息
    WorkInfoSet, err := ReadCsvFile(csvName, employeeName)
    if err != nil {
        p(err.Error())
        return
    }

    //把指定员工信息写入json文件
    err = WriteJsonFile(employeeName, WorkInfoSet)
    if err != nil {
        p(err.Error())
        return
    }

    //读取json文件并计算指定员工总工时和平均工时
    err = ReadJsonFile(employeeName)
    if err != nil {
        p(err.Error())
        return
    }
}

README.md

- USAGE: Analysis csv file command [arguments] ...

- The commands are:
-    -h , --help    cmd help.

- The commands are:
-    -i  input employee name.

- The commands are:
-    -p  input csv file path.

-当文件中不存在指定员工名字时,返回提醒信息

-参考链接:
- Golang GBK转UTF-8 参考链接:https://blog.csdn.net/qq_33285730/article/details/73239263
- golang 文件按行读取:https://studygolang.com/articles/282
- golang strings包方法:https://studygolang.com/articles/2881

附:使用Golang导出CSV数据并解决数据乱码问题

在日常开发中,针对数据导出,我们可以导出Excel格式,但是如果是针对大数据量的导出,直接导出为Excel格式可能需要占用大量内存,且导出速度很慢。这个时候我们就需要导出为CSV格式。

CSV 格式

CSV本质上是文本文件,该文件有以下要求:

  • 列之间用逗号分隔,行之间用换行分隔
  • 单元格如果有逗号、引号之类的字符,该单元格需要使用双引号括起来
  • 如果内容包含中文,直接输出可能会乱码

实现方式

golang 官方有csv的库,可以很容易的实现csv数据的写入。

golang实现csv数据写文件

func main() {
	f, err := os.Create("data.csv")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	f.WriteString("\xEF\xBB\xBF") // 写入UTF-8 BOM,避免使用Microsoft Excel打开乱码

	writer := csv.NewWriter(f)
	writer.Write([]string{"编号", "姓名", "年龄"})
	writer.Write([]string{"1", "张三", "23"})
	writer.Write([]string{"2", "李四", "24"})
	writer.Write([]string{"3", "王五", "25"})
	writer.Write([]string{"4", "赵六", "26"})
	writer.Flush() // 此时才会将缓冲区数据写入
}

golang实现web导出csv数据

此处以gin框架为例,如果用的go官方web库,其实差不多是一样的:

func ExportCsv(c *gin.Context) {
	bytesBuffer := &bytes.Buffer{}
	bytesBuffer.WriteString("\xEF\xBB\xBF") // 写入UTF-8 BOM,避免使用Microsoft Excel打开乱码

	writer := csv.NewWriter(bytesBuffer)
	writer.Write([]string{"编号", "姓名", "年龄"})
	writer.Write([]string{"1", "张三", "23"})
	writer.Write([]string{"2", "李四", "24"})
	writer.Write([]string{"3", "王五", "25"})
	writer.Write([]string{"4", "赵六", "26"})

	writer.Flush() // 此时才会将缓冲区数据写入

	// 设置下载的文件名
	c.Writer.Header().Set("Content-Disposition", "attachment;filename=data.csv")
	// 设置文件类型以及输出数据
	c.Data(http.StatusOK, "text/csv", bytesBuffer.Bytes())
	return
}

总结

到此这篇关于golang如何操作csv文件的文章就介绍到这了,更多相关golang操作csv文件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • go语言读取csv文件并输出的方法

    本文实例讲述了go语言读取csv文件并输出的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package main import (     "encoding/csv"     "fmt"     "io"     "os" ) func main() {     file, err := os.Open("names.txt")     if err != nil {      

  • golang如何操作csv文件详解

    目录 练习要求: 考察点: 编码: README.md 附:使用Golang导出CSV数据并解决数据乱码问题 CSV 格式 实现方式 golang实现csv数据写文件 golang实现web导出csv数据 总结 练习要求: 写一个小程序解析data.csv,要求实现如下功能: 接收姓名作为参数. 根据姓名查找出对应员工的工时信息,并将 日期.上班.下班.工时 打印到标准输出. 将上一条输出的内容保存到json文件,使用姓名.json作为文件名 根据上条中生成的json文件,计算出该员工的月总工时

  • 详解Python读取和写入操作CSV文件的方法

    目录 什么是 CSV 文件? 内置 CSV 库解析 CSV 文件 读取 CSV 文件csv 将 CSV 文件读入字典csv 可选的 Python CSV reader参数 使用 csv 写入文件 从字典中写入 CSV 文件csv 使用 pandas 库解析 CSV 文件 pandas 读取 CSV 文件 pandas 写入 CSV 文件 最流行的数据交换格式之一是 CSV 格式.是需要通过键盘和控制台以外的方式将信息输入和输出的程序,通过文本文件交换信息是在程序之间共享信息的常用方法. 这里带和

  • Servlet中操作文件详解及实例

    Servlet中操作文件详解及实例 因为Servlet本来就是一个.Java文件,因此servlet中操作文件和普通java文件操作文件是一样的. 读取文件主要代码: FileReader f=new FileReader("f:\\lissdy.txt"); BufferedReader bw=new BufferedReader(f); 读出一行数据 String num=bw.readLine(); 注意一定要关闭文件流 bw.close(); 写文件的方法与之类似,具体代码为:

  • Python常见文件操作的示例详解

    目录 从文件中读取数据 为什么要提供文件路径 逐行读取 创建一个包含文件各行内容的列表 使用文件中的内容 包含千位以上的大型文件 圆周率中包含你的生日吗 写入文件 附加到文件 从文件中读取数据 1:读取整个文件 首先创建一个文件,它包含一些文本信息,注意:如果该文件为.py文件,则再进行操作的时候可以不写路径,如果不是.py文件,则必须标明路径. 现在我们创建一个python文件,名为text.py,给他写入如下文本信息: 下面的程序打开并读取这个文件,再将其内容显示到屏幕上: with ope

  • Node文件操作汇总实例详解

    目录 前言 文件系统 目录处理 读写文件 文件描述 监控文件 异步文件 删除文件 总结 前言 前两天我们介绍了 Nodejs 中 I/O 的重要组成部分:Buffer对象 和 Stream流,今天我们介绍 Nodejs 的文件系统,包括文件和目录的读写.删除,以及监听文件和目录的修改. 文件系统 Nodejs 内置的 fs模块 可与文件系统进行交互,封装了常规的 POSIX函数.POSIX(Portable Operating System Interface,可移植操作系统接口)是UNIX系统

  • C语言中文件常见操作的示例详解

    目录 文件打开和关闭 文件写入 文件读取 fseek函数 ftell函数 Demo示例 解决读取乱码 FILE为C语言提供的文件类型,它是一个结构体类型,用于存放文件的相关信息.文件打开成功时,对它作了内存分配和初始化. 每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节. 一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便. 文件打开和关闭 C语言的安全文件打开函数为_wfopen_s和_fopen_s

  • 对Golang import 导入包语法详解

    package 的导入语法 写 Go 代码的时经常用到 import 这个命令用来导入包,参考如下: import( "fmt" ) 然后在代码里面可以通过如下的方式调用: fmt.Println( "我爱北京天安门" ) fmt 是 Go 的标准库,它其实是去 GOROOT 下去加载该模块,当然 Go 的 import 还支持如下两种方式来加载自己写的模块: 相对路径 import "./model" // 当前文件同一目录的 model 目录

  • Python批量操作Excel文件详解

    目录 批量操作 OS模块介绍 OS模块基本操作 获取当前工作路径 获取一个文件夹下的所有文件名 对文件名进行重命名 创建一个文件夹 删除一个文件夹 删除一个文件 利用OS模块进行批量操作 批量读取一个文件下的多个文件 批量创建文件夹 批量重命名文件 其他批量操作 批量合并多个文件 将一份文件按照指定列拆分成多个文件 批量操作 OS模块介绍 OS的全称是Operation System,指操作系统.在Python里面OS模块中主要提供了与操作系统即电脑系统之间进行交互的一些功能.我们很多的自动化操

  • golang字符串本质与原理详解

    目录 一.字符串的本质 1.字符串的定义 2.字符串的长度 3.字符与符文 二.字符串的原理 1.字符串的解析 2.字符串的拼接 3.字符串的转换 总结 一.字符串的本质 1.字符串的定义 golang中的字符(character)串指的是所有8比特位字节字符串的集合,通常(非必须)是UTF-8 编码的文本. 字符串可以为空,但不能是nil. 字符串在编译时即确定了长度,值是不可变的. // go/src/builtin/builtin.go // string is the set of al

  • golang构建工具Makefile使用详解

    目录 正文 正文 可能是因为编译太简单了,golang 并没有一个官方的构建工具(类似于 java 的 maven 和 gradle之类的),但是除了编译,我们可能还需要下载依赖,运行测试,甚至像 easyjson,protobuf,thrift 这样的工具下载和代码生成,如果没有构建工具,这些工作就会非常麻烦 为了解决这个问题,之前写过一个 everything.sh 的脚本,把所有的操作都封装在这个脚本里面,只需要执行类似于 sh everything.sh dependency 的命令就可

随机推荐