golang如何操作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 }
总结
上一篇:GO语言类型查询类型断言示例解析
栏 目:Golang
本文标题:golang如何操作csv文件详解
本文地址:http://www.codeinn.net/misctech/226509.html