目录
  • 练习要求:
  • 考察点:
  • 编码:
  • readme.md
  • 附:使用golang导出csv数据并解决数据乱码问题
    • csv 格式
    • 实现方式
    • golang实现csv数据写文件
    • golang实现web导出csv数据
  • 总结

    练习要求:

    写一个小程序解析data.csv,要求实现如下功能:

    1. 接收姓名作为参数。
    2. 根据姓名查找出对应员工的工时信息,并将 日期、上班、下班、工时 打印到标准输出。
    3. 将上一条输出的内容保存到json文件,使用姓名.json作为文件名
    4. 根据上条中生成的json文件,计算出该员工的月总工时、每周的平均工时。

    考察点:

    1. 结构体定义
    2. 字符串拼接
    3. 类型转换
    4. 编码转换
    5. 命令行参数解析
    6. 文件读取
    7. 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 //异常信息
    }
    /**
    * @brief  把当前字符串按照指定方式进行编码
    * @param[in]       src                   待进行转码的字符串
    * @param[in]       srccode               字符串当前编码
    * @param[in]       tagcode               要转换的编码
    * @return   进行转换后的字符串
    */
    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            写入数据到指定名字的文件中
    * @param[in]       buf                    待写入的数据内容
    * @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   成功返回 文件内容,失败返回error    错误信息
    */
    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
    }
    /**
    * @brief  读取csv文件并打印指定员工信息
    * @param[in]       csvname            csv文件名(可以加路径)
    * @param[in]       employeename        员工名字
    * @return   成功返回 员工结构体信息,失败返回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'为结束符读入一行
    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
    }
    /**
    * @brief  写入json文件
    * @param[in]       employeename        员工名字
    * @param[in]       workinfoset            员工结构体信息
    * @return   成功返回 nil,失败返回error    错误信息
    */
    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
    }
    /**
    * @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 {
    //去掉打卡异常情况和周六末情况 (如果周六末加班 数据依然计算进入总工时)
    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文件内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!