为了账号安全,请及时绑定邮箱和手机立即绑定

Go 中 CSV 和地图的性能不佳

Go 中 CSV 和地图的性能不佳

Go
拉莫斯之舞 2021-08-30 21:59:34
我需要编写一个 Go 脚本来打开一个大的 CSV 文件,并根据每行第一个元素的值创建新的、单独的 CSV。CSV 文件如下所示:"country", "otherfield", "otherfield1", "otherfield2", "etc""AT", "otherfield", "otherfield1", "otherfield2", "etc""AT", "otherfield", "otherfield1", "otherfield2", "etc""DE", "otherfield", "otherfield1", "otherfield2", "etc""DE", "otherfield", "otherfield1", "otherfield2", "etc"所以,我想要做的是创建一个文件AT.csv,其中包含第一个字段的值(例如),其中包含以该值开头的所有行。我目前遇到的问题是表演很慢。我什至有一个用 PHP 编写的类似脚本,它以比这更快的方式执行相同的操作。这显然让我觉得我的 Go 脚本肯定有问题。有人可以帮助我了解它有什么问题吗?
查看完整描述

2 回答

?
红颜莎娜

TA贡献1842条经验 获得超12个赞

我支持@plusmid 的回答——你的程序大部分时间都花在打开/(覆盖)写入/关闭文件上。


所以,首先修复这个bug,每个key只写一次内容:


package main


import (

    "encoding/csv"

    "fmt"

    "os"

)


func main() {


    // contentCreated := make(chan map[string]string)


    createContent("union_exp.csv")


}


func createContent(csvfilename string) {


    keys := ""


    content := make(map[string]string)


    csvfile, err := os.Open(csvfilename)


    if err != nil {

        fmt.Println(err)

    }


    defer csvfile.Close()


    reader := csv.NewReader(csvfile)


    reader.FieldsPerRecord = -1


    rawCSVdata, err := reader.ReadAll()


    if err != nil {

        fmt.Println(err)

        os.Exit(1)

    }


    for i, each := range rawCSVdata {


        if i == 0 {

            keys = "\"" + each[0] + "\",\"" + each[1] + "\",\"" + each[2] + "\",\"" + each[3] + "\",\"" + each[4] + "\"\n"

        } else {


            stringtoadd := "\"" + each[0] + "\",\"" + each[1] + "\",\"" + each[2] + "\",\"" + each[3] + "\",\"" + each[4] + "\"\n"


            if i%10000 == 0 {

                fmt.Println(i)

            }


            exists := Exists(content, each[0])

            if !exists {

                content[each[0]] = keys

            }


            content[each[0]] += stringtoadd

        }

    }


    for key, content := range content {

        createFile(key, content)

    }

}


func createFile(name, content string) {


    f, _ := os.Create(name + ".csv")

    f.WriteString(content)

    f.Close()

}


func Exists(content map[string]string, name string) bool {

    _, exists := content[name]

    return exists

}

在 25k CSV 上,它给了我 50 -> 5 秒的速度提升。


接下来考虑使用goroutines并行解析文件。现在您只使用单核。此外,还有一些问题,例如使用 + 运算符连接字符串,这通常比 fmt.Sprintf() 慢。您有很多空间可以在这里优化代码。


查看完整回答
反对 回复 2021-08-30
  • 2 回答
  • 0 关注
  • 207 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信