Golang实战:解析CSV考勤数据并导出JSON工时统计
本文详细介绍如何利用Go语言解析包含考勤数据的CSV文件,将指定员工信息提取并存储为JSON,最终计算月总工时与周均工时。文章涵盖完整的业务逻辑代码实现、编码转换技巧,以及使用标准库和Gin框架进行CSV导出的方法,重点解决文件乱码与大文件内存优化问题。
场景目标与核心功能
在处理企业内部考勤或日志数据时,CSV因其轻量且易于生成而广泛使用。本实例旨在开发一个命令行工具,具备以下核心能力:
- 参数化输入:支持通过命令行传入员工姓名和目标CSV文件路径。
- 数据解析与筛选:读取CSV原始数据,自动识别表头,精准提取特定员工的日期、上下班时间及工时信息。
- 持久化存储:将筛选后的结构化数据序列化,输出为标准JSON文件供后续系统调用。
- 统计分析:基于已生成的JSON数据,剔除异常打卡记录,计算该员工的月度总工时及每周平均工时。
第1步:定义数据结构与环境依赖
首先需引入必要的包,包括用于文件操作的 os 和 bufio,用于编码处理的第三方库(如用于处理GBK编码的 mahonia),以及标准的 encoding/json 和 flag 库。定义结构体来映射CSV中的“上班信息”与“考勤异常信息”,确保数据模型清晰。
package main
import (
"bufio"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"os"
"strconv"
"strings"
"github.com/axgle/mahonia" // 处理非UTF-8编码转换
)
type WorkInfo struct {
WorkDate string
StartTime string
EndTime string
LaborHour string
}
type WorkAbnormalInfo struct {
WorkDate string
NormalInfo string
}
第2步:实现编码转换与文件读写辅助函数
许多国内遗留系统的CSV文件采用GB2312或GBK编码,直接读取会导致乱码。需要编写辅助函数将源编码转换为UTF-8。同时,封装通用的文件读取(全部加载)与写入接口,简化主流程逻辑。
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)
return string(cdata), nil
}
func WriteFile(name string, buf string) 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
}
第3步:解析CSV并筛选员工考勤信息
此为核心步骤。程序打开文件流,逐行读取。针对首行表头动态匹配列索引(如“姓名”、“工时”等)。在后续数据行中,利用 strings.Contains 判断是否为目标员工。若命中,则按列索引拆分字符串并组装成结构体切片。
func ReadCsvFile(csvName string, employeeName string) ([]WorkInfo, error) {
f, err := os.Open(csvName)
if err != nil { return nil, err }
defer f.Close()
rd := bufio.NewReader(f)
var WorkInfoSet []WorkInfo
i := 0
for {
gbk_line, err := rd.ReadString('\n')
if err != nil || io.EOF == err { break }
utf8_line, _ := ConvertToString(gbk_line, "gbk", "utf-8")
// 处理表头,建立字段名到索引的映射
if i == 0 {
i++
first_line := strings.Split(utf8_line, ",")
// ...此处省略具体的列索引赋值逻辑,实际运行需对应列名
}
// 匹配员工名称
if strings.Contains(utf8_line, employeeName) {
split_line := strings.Split(utf8_line, ",")
person_temp := WorkInfo{
WorkDate: split_line[indexWorkDate],
StartTime: split_line[indexStartTime],
EndTime: split_line[indexEndTime],
LaborHour: split_line[indexLaborHour],
}
WorkInfoSet = append(WorkInfoSet, person_temp)
}
}
return WorkInfoSet, nil
}
第4步:序列化为JSON文件
获取员工考勤列表后,利用 json.Marshal 将其转换为JSON字节数组,再调用文件写入函数保存为 [员工姓名].json。这步实现了业务数据的状态快照,便于后续独立处理。
第5步:基于JSON进行工时统计计算
重新读取上一步生成的JSON文件,反序列化回Go结构体。遍历数据进行聚合计算:累加有效工时(跳过开始/结束时间为空的异常记录),统计天数,并推导周数(总天数除以7)来计算周平均工时。
func ReadJsonFile(employeeName string) error {
filename := fmt.Sprintf("%s.json", employeeName)
ReadJsonBuf, _ := os.ReadFile(filename) // Go 1.16+ 推荐用法
var WorkInfoSet []WorkInfo
json.Unmarshal(ReadJsonBuf, &WorkInfoSet)
var sumHour float64
for _, one_work := range WorkInfoSet {
if one_work.StartTime == "" || one_work.EndTime == "" {
continue // 忽略异常打卡
}
hour, _ := strconv.ParseFloat(one_work.LaborHour, 64)
sumHour += hour
}
// 计算周平均...略
return nil
}
Gin Web环境下的CSV数据导出方案
除了读取,在实际Web开发中常需将数据库结果导出为CSV。对于大数据量场景,Excel格式内存占用极高,CSV是更优选择。以下是两种常见的导出实现方式。
本地文件生成与BOM头处理
Windows下的Excel默认以ANSI或GBK解析无标记文本,直接输出的中文会乱码。解决标准做法是在文件头部插入 \xEF\xBB\xBF (UTF-8 BOM),强制Excel以UTF-8模式打开。配合 csv.NewWriter 高效写入。
func generateLocalCsv() {
f, err := os.Create("data.csv")
if err != nil { panic(err) }
defer f.Close()
// 写入BOM头防止Excel中文乱码
f.WriteString("\xEF\xBB\xBF")
writer := csv.NewWriter(f)
writer.Write([]string{"编号", "姓名"})
writer.Write([]string{"1", "张三"})
writer.Flush() // 必须Flush才会生效
}
HTTP响应流式导出(Gin框架示例)
在Web应用中,通常不落地生成文件,而是直接将数据流写入HTTP Response Header。关键设置 Content-Disposition 触发浏览器下载行为,并使用 bytes.Buffer 作为中转缓冲区。
func ExportCsv(c *gin.Context) {
buf := &bytes.Buffer{}
buf.WriteString("\xEF\xBB\xBF") // UTF-8 BOM
writer := csv.NewWriter(buf)
writer.Write([]string{"ID", "姓名"})
writer.Write([]string{"1", "李四"})
writer.Flush()
c.Header("Content-Type", "text/csv")
c.Header("Content-Disposition", "attachment; filename=export.csv")
c.Data(http.StatusOK, "text/csv", buf.Bytes())
}
以上就是Go语言操作CSV文件的详细内容,更多关于Go语言处理考勤数据与文件导出的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









