练习要求:
写一个小程序解析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 //异常信息
}
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
}
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
}
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
}
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
}
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
}
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文件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!