golang如何操作csv文件詳解
練習要求:
寫一個小程序解析data.csv,要求實現(xiàn)如下功能:
- 接收姓名作為參數(shù)。
- 根據(jù)姓名查找出對應(yīng)員工的工時信息,并將 日期、上班、下班、工時 打印到標準輸出。
- 將上一條輸出的內(nèi)容保存到j(luò)son文件,使用姓名.json作為文件名
- 根據(jù)上條中生成的json文件,計算出該員工的月總工時、每周的平均工時。
考察點:
- 結(jié)構(gòu)體定義
- 字符串拼接
- 類型轉(zhuǎn)換
- 編碼轉(zhuǎn)換
- 命令行參數(shù)解析
- 文件讀取
- json庫使用
編碼:
package main import ( "bufio" "encoding/json" "errors" "flag" "fmt" "io" "os" "strconv" "strings" "github.com/axgle/mahonia" ) //給 fmt.Println 起一個短的別名。 var p = fmt.Println //定義一個全局變量 一個月上班加休息總天數(shù) var gAllDays float64 = 0 //定義一個全局變量 考勤異常的天數(shù) 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 待進行轉(zhuǎn)碼的字符串 * @param[in] srcCode 字符串當前編碼 * @param[in] tagCode 要轉(zhuǎn)換的編碼 * @return 進行轉(zhuǎn)換后的字符串 */ 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 寫入數(shù)據(jù)到指定名字的文件中 * @param[in] buf 待寫入的數(shù)據(jù)內(nèi)容 * @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 成功返回 文件內(nèi)容,失敗返回error 錯誤信息 */ func ReadFile(name string) ([]byte, error) { if len(name) == 0 { return nil, errors.New("input arguments error") } //打開本地文件 讀取出全部數(shù)據(jù) 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 成功返回 員工結(jié)構(gòu)體信息,失敗返回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'為結(jié)束符讀入一行 if err != nil || io.EOF == err { break } //p("gbk:", gbk_line) //把每一行g(shù)bk格式的字符串 轉(zhuǎn)換為 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], } //考勤表天數(shù)加1 dayCount++ isNormal = split_line[indexIsNormal] //統(tǒng)計打卡異常的信息 if isNormal == "是" { aInfo := WorkAbnormalInfo{split_line[indexWorkDate], split_line[indexNormalInfo]} AbnormalSet = append(AbnormalSet, aInfo) gAbnormalDays++ isNormalFlag = true } WorkInfoSet = append(WorkInfoSet, person_temp) } } //統(tǒng)計考勤表里所有天數(shù) 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("溫馨提示:考勤出現(xiàn)異常信息,請及時給助理說明情況~_~\n") } return WorkInfoSet, nil } /** * @brief 寫入json文件 * @param[in] employeeName 員工名字 * @param[in] workInfoSet 員工結(jié)構(gòu)體信息 * @return 成功返回 nil,失敗返回error 錯誤信息 */ func WriteJsonFile(employeeName string, workInfoSet []WorkInfo) error { if len(employeeName) == 0 || workInfoSet == nil { return errors.New("error: input arguments error") } //把輸出內(nèi)容寫入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 { //去掉打卡異常情況和周六末情況 (如果周六末加班 數(shù)據(jù)依然計算進入總工時) if one_work.StartTime == "" || one_work.EndTime == "" { continue } one_day_hour, _ := strconv.ParseFloat(one_work.LaborHour, 64) sumHour += one_day_hour dayCount++ } fmt.Printf("根據(jù)json文件計算工時,考勤正常天數(shù):%2.0f, 異常天數(shù):%d\n", dayCount, gAbnormalDays) weekCounts = gAllDays / 7 averageWeekHour = sumHour / weekCounts //p("考勤表總天數(shù):", 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轉(zhuǎn)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導(dǎo)出CSV數(shù)據(jù)并解決數(shù)據(jù)亂碼問題
在日常開發(fā)中,針對數(shù)據(jù)導(dǎo)出,我們可以導(dǎo)出Excel格式,但是如果是針對大數(shù)據(jù)量的導(dǎo)出,直接導(dǎo)出為Excel格式可能需要占用大量內(nèi)存,且導(dǎo)出速度很慢。這個時候我們就需要導(dǎo)出為CSV格式。
CSV 格式
CSV本質(zhì)上是文本文件,該文件有以下要求:
- 列之間用逗號分隔,行之間用換行分隔
- 單元格如果有逗號、引號之類的字符,該單元格需要使用雙引號括起來
- 如果內(nèi)容包含中文,直接輸出可能會亂碼
實現(xiàn)方式
golang 官方有csv的庫,可以很容易的實現(xiàn)csv數(shù)據(jù)的寫入。
golang實現(xiàn)csv數(shù)據(jù)寫文件
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() // 此時才會將緩沖區(qū)數(shù)據(jù)寫入 }
golang實現(xiàn)web導(dǎo)出csv數(shù)據(jù)
此處以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() // 此時才會將緩沖區(qū)數(shù)據(jù)寫入 // 設(shè)置下載的文件名 c.Writer.Header().Set("Content-Disposition", "attachment;filename=data.csv") // 設(shè)置文件類型以及輸出數(shù)據(jù) c.Data(http.StatusOK, "text/csv", bytesBuffer.Bytes()) return }
總結(jié)
到此這篇關(guān)于golang如何操作csv文件的文章就介紹到這了,更多相關(guān)golang操作csv文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
自己動手用Golang實現(xiàn)約瑟夫環(huán)算法的示例
這篇文章主要介紹了自己動手用Golang實現(xiàn)約瑟夫環(huán)算法的示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2019-12-12Go語言中轉(zhuǎn)換JSON數(shù)據(jù)簡單例子
這篇文章主要介紹了Go語言中轉(zhuǎn)換JSON數(shù)據(jù)簡單例子,本文先定義了一個結(jié)構(gòu)體,然后把JSON綁定到結(jié)構(gòu)體上實現(xiàn)讀取,需要的朋友可以參考下2014-10-10云端golang開發(fā),無需本地配置,能上網(wǎng)就能開發(fā)和運行
這篇文章主要介紹了云端golang開發(fā),無需本地配置,能上網(wǎng)就能開發(fā)和運行的相關(guān)資料,需要的朋友可以參考下2023-10-10