golang如何操作csv文件详解
目录
- 练习要求:
- 考察点:
- 编码:
- README.md
- 附:使用Golang导出CSV数据并解决数据乱码问题
- CSV 格式
- 实现方式
- golang实现csv数据写文件
- golang实现web导出csv数据
- 总结
练习要求:
写一个小程序解析data.csv,要求实现如下功能:
- 接收姓名作为参数。
- 根据姓名查找出对应员工的工时信息,并将 日期、上班、下班、工时 打印到标准输出。
- 将上一条输出的内容保存到json文件,使用姓名.json作为文件名
- 根据上条中生成的json文件,计算出该员工的月总工时、每周的平均工时。
考察点:
- 结构体定义
- 字符串拼接
- 类型转换
- 编码转换
- 命令行参数解析
- 文件读取
- 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)