Golang实战:解析CSV考勤数据并导出JSON工时统计

小鹿同学
小鹿同学
发布时间:2026-10-16 | 6人浏览过
原创

本文详细介绍如何利用Go语言解析包含考勤数据的CSV文件,将指定员工信息提取并存储为JSON,最终计算月总工时与周均工时。文章涵盖完整的业务逻辑代码实现、编码转换技巧,以及使用标准库和Gin框架进行CSV导出的方法,重点解决文件乱码与大文件内存优化问题。

场景目标与核心功能

在处理企业内部考勤或日志数据时,CSV因其轻量且易于生成而广泛使用。本实例旨在开发一个命令行工具,具备以下核心能力:

  1. 参数化输入:支持通过命令行传入员工姓名和目标CSV文件路径。
  2. 数据解析与筛选:读取CSV原始数据,自动识别表头,精准提取特定员工的日期、上下班时间及工时信息。
  3. 持久化存储:将筛选后的结构化数据序列化,输出为标准JSON文件供后续系统调用。
  4. 统计分析:基于已生成的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语言处理考勤数据与文件导出的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表