golang如何操作csv文件詳解
練習(xí)要求:
寫一個(gè)小程序解析data.csv,要求實(shí)現(xiàn)如下功能:
- 接收姓名作為參數(shù)。
- 根據(jù)姓名查找出對(duì)應(yīng)員工的工時(shí)信息,并將 日期、上班、下班、工時(shí) 打印到標(biāo)準(zhǔn)輸出。
- 將上一條輸出的內(nèi)容保存到j(luò)son文件,使用姓名.json作為文件名
- 根據(jù)上條中生成的json文件,計(jì)算出該員工的月總工時(shí)、每周的平均工時(shí)。
考察點(diǎn):
- 結(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 起一個(gè)短的別名。
var p = fmt.Println
//定義一個(gè)全局變量 一個(gè)月上班加休息總天數(shù)
var gAllDays float64 = 0
//定義一個(gè)全局變量 考勤異常的天數(shù)
var gAbnormalDays int = 0
//上班信息
type WorkInfo struct {
WorkDate string //上班日期
StartTime string //上班打卡時(shí)間
EndTime string //下班打卡時(shí)間
LaborHour string //當(dāng)天工時(shí)
}
//考勤異常信息
type WorkAbnormalInfo struct {
WorkDate string //上班日期
NormalInfo string //異常信息
}
/**
* @brief 把當(dāng)前字符串按照指定方式進(jìn)行編碼
* @param[in] src 待進(jìn)行轉(zhuǎn)碼的字符串
* @param[in] srcCode 字符串當(dāng)前編碼
* @param[in] tagCode 要轉(zhuǎn)換的編碼
* @return 進(jìn)行轉(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 錯(cuò)誤信息
*/
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 錯(cuò)誤信息
*/
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 錯(cuò)誤信息
*/
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")
//對(duì)第一行進(jìn)行處理
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 == "工時(shí)" {
indexLaborHour = index
}
if val == "是否有考勤異常" {
indexIsNormal = index
}
if val == "工時(shí)異常" {
indexNormalInfo = index
}
index++
}
}
if strings.Contains(utf8_line, employeeName) {
//把存在員工標(biāo)記為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)計(jì)打卡異常的信息
if isNormal == "是" {
aInfo := WorkAbnormalInfo{split_line[indexWorkDate], split_line[indexNormalInfo]}
AbnormalSet = append(AbnormalSet, aInfo)
gAbnormalDays++
isNormalFlag = true
}
WorkInfoSet = append(WorkInfoSet, person_temp)
}
}
//統(tǒng)計(jì)考勤表里所有天數(shù)
gAllDays = dayCount
//對(duì)于不存在指定員工名字 的處理
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,工時(shí):%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)異常信息,請(qǐng)及時(shí)給助理說明情況~_~\n")
}
return WorkInfoSet, nil
}
/**
* @brief 寫入json文件
* @param[in] employeeName 員工名字
* @param[in] workInfoSet 員工結(jié)構(gòu)體信息
* @return 成功返回 nil,失敗返回error 錯(cuò)誤信息
*/
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 錯(cuò)誤信息
*/
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ù)依然計(jì)算進(jìn)入總工時(shí))
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文件計(jì)算工時(shí),考勤正常天數(shù):%2.0f, 異常天數(shù):%d\n", dayCount, gAbnormalDays)
weekCounts = gAllDays / 7
averageWeekHour = sumHour / weekCounts
//p("考勤表總天數(shù):", gAllDays, ",共多少周:", week_counts)
fmt.Printf("月總工時(shí):%.4f 每周的平均工時(shí):%.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文件并計(jì)算指定員工總工時(shí)和平均工時(shí)
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.-當(dāng)文件中不存在指定員工名字時(shí),返回提醒信息
-參考鏈接:
- Golang GBK轉(zhuǎn)UTF-8 參考鏈接:https://blog.csdn.net/qq_33285730/article/details/73239263
- golang 文件按行讀?。篽ttps://studygolang.com/articles/282
- golang strings包方法:https://studygolang.com/articles/2881
附:使用Golang導(dǎo)出CSV數(shù)據(jù)并解決數(shù)據(jù)亂碼問題
在日常開發(fā)中,針對(duì)數(shù)據(jù)導(dǎo)出,我們可以導(dǎo)出Excel格式,但是如果是針對(duì)大數(shù)據(jù)量的導(dǎo)出,直接導(dǎo)出為Excel格式可能需要占用大量?jī)?nèi)存,且導(dǎo)出速度很慢。這個(gè)時(shí)候我們就需要導(dǎo)出為CSV格式。
CSV 格式
CSV本質(zhì)上是文本文件,該文件有以下要求:
- 列之間用逗號(hào)分隔,行之間用換行分隔
- 單元格如果有逗號(hào)、引號(hào)之類的字符,該單元格需要使用雙引號(hào)括起來
- 如果內(nèi)容包含中文,直接輸出可能會(huì)亂碼
實(shí)現(xiàn)方式
golang 官方有csv的庫,可以很容易的實(shí)現(xiàn)csv數(shù)據(jù)的寫入。
golang實(shí)現(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") // 寫入U(xiǎn)TF-8 BOM,避免使用Microsoft Excel打開亂碼
writer := csv.NewWriter(f)
writer.Write([]string{"編號(hào)", "姓名", "年齡"})
writer.Write([]string{"1", "張三", "23"})
writer.Write([]string{"2", "李四", "24"})
writer.Write([]string{"3", "王五", "25"})
writer.Write([]string{"4", "趙六", "26"})
writer.Flush() // 此時(shí)才會(huì)將緩沖區(qū)數(shù)據(jù)寫入
}golang實(shí)現(xiàn)web導(dǎo)出csv數(shù)據(jù)
此處以gin框架為例,如果用的go官方web庫,其實(shí)差不多是一樣的:
func ExportCsv(c *gin.Context) {
bytesBuffer := &bytes.Buffer{}
bytesBuffer.WriteString("\xEF\xBB\xBF") // 寫入U(xiǎn)TF-8 BOM,避免使用Microsoft Excel打開亂碼
writer := csv.NewWriter(bytesBuffer)
writer.Write([]string{"編號(hào)", "姓名", "年齡"})
writer.Write([]string{"1", "張三", "23"})
writer.Write([]string{"2", "李四", "24"})
writer.Write([]string{"3", "王五", "25"})
writer.Write([]string{"4", "趙六", "26"})
writer.Flush() // 此時(shí)才會(huì)將緩沖區(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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語言interface接口繼承多態(tài)示例及定義解析
這篇文章主要為大家介紹了go語言interface接口繼承多態(tài)示例及定義解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
go交叉編譯sqlite報(bào)錯(cuò)問題解決分析
這篇文章主要為大家介紹了go交叉編譯sqlite報(bào)錯(cuò)問題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
Go語言獲取系統(tǒng)性能數(shù)據(jù)gopsutil庫的操作
這篇文章主要介紹了Go語言獲取系統(tǒng)性能數(shù)據(jù)gopsutil庫的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12
使用Go語言實(shí)現(xiàn)發(fā)送HTTP請(qǐng)求并給GET添加參數(shù)
在開發(fā)Web應(yīng)用程序時(shí),我們經(jīng)常需要向服務(wù)器發(fā)送HTTP請(qǐng)求,本文將介紹一下使用Go語言發(fā)送HTTP請(qǐng)求,并給GET請(qǐng)求添加參數(shù)的方法,感興趣的小伙伴可以了解一下2023-07-07
Golang String字符串類型轉(zhuǎn)Json格式
本文主要介紹了Golang String字符串類型轉(zhuǎn)Json格式的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05

