在Go中高效读写CSV
下面的Go代码读取10000条CSV记录(时间戳在Go中高效读写CSV,csv,go,Csv,Go,下面的Go代码读取10000条CSV记录(时间戳时间和浮点值),对数据运行一些操作,然后将原始值连同分数的附加列写入另一个CSV。但是它非常慢(即数小时,但大部分时间都是calculateStuff()),我很好奇CSV读/写是否有效率低下的问题 package main import ( "encoding/csv" "log" "os" "strconv" ) func ReadCSV(filepath string) ([][]string, error) { cs
时间
和浮点值
),对数据运行一些操作,然后将原始值连同分数
的附加列写入另一个CSV。但是它非常慢(即数小时,但大部分时间都是calculateStuff()
),我很好奇CSV读/写是否有效率低下的问题
package main
import (
"encoding/csv"
"log"
"os"
"strconv"
)
func ReadCSV(filepath string) ([][]string, error) {
csvfile, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer csvfile.Close()
reader := csv.NewReader(csvfile)
fields, err := reader.ReadAll()
return fields, nil
}
func main() {
// load data csv
records, err := ReadCSV("./path/to/datafile.csv")
if err != nil {
log.Fatal(err)
}
// write results to a new csv
outfile, err := os.Create("./where/to/write/resultsfile.csv"))
if err != nil {
log.Fatal("Unable to open output")
}
defer outfile.Close()
writer := csv.NewWriter(outfile)
for i, record := range records {
time := record[0]
value := record[1]
// skip header row
if i == 0 {
writer.Write([]string{time, value, "score"})
continue
}
// get float values
floatValue, err := strconv.ParseFloat(value, 64)
if err != nil {
log.Fatal("Record: %v, Error: %v", floatValue, err)
}
// calculate scores; THIS EXTERNAL METHOD CANNOT BE CHANGED
score := calculateStuff(floatValue)
valueString := strconv.FormatFloat(floatValue, 'f', 8, 64)
scoreString := strconv.FormatFloat(prob, 'f', 8, 64)
//fmt.Printf("Result: %v\n", []string{time, valueString, scoreString})
writer.Write([]string{time, valueString, scoreString})
}
writer.Flush()
}
我正在寻找帮助,使这个CSV读/写模板代码尽可能快。对于这个问题的范围,我们不必担心
calculateStuff
方法。编码/csv
在大文件上确实非常慢,因为它执行大量分配。由于格式非常简单,我建议使用strings.Split
,这会更快
如果这不够快,您可以考虑使用<代码>字符串来实现解析。
话虽如此,如果文件比内存大,您也应该重新考虑使用
ReadAll
。您首先将文件加载到内存中,然后再处理它,这对于大文件来说可能会很慢
您需要循环并调用。读取并一次处理一行
func processCSV(rc io.Reader) (ch chan []string) {
ch = make(chan []string, 10)
go func() {
r := csv.NewReader(rc)
if _, err := r.Read(); err != nil { //read header
log.Fatal(err)
}
defer close(ch)
for {
rec, err := r.Read()
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
ch <- rec
}
}()
return
}
func processCSV(rc io.Reader)(ch chan[]字符串){
ch=制造(chan[]字符串,10)
go func(){
r:=csv.NewReader(rc)
如果{,err:=r.Read();err!=nil{//Read头
log.Fatal(错误)
}
延迟关闭(ch)
为了{
rec,err:=r.Read()
如果错误!=零{
如果err==io.EOF{
打破
}
log.Fatal(错误)
}
ch这基本上是作者在评论部分的回答:
package main
import (
"encoding/csv"
"log"
"os"
"strconv"
)
func main() {
// setup reader
csvIn, err := os.Open("./path/to/datafile.csv")
if err != nil {
log.Fatal(err)
}
r := csv.NewReader(csvIn)
// setup writer
csvOut, err := os.Create("./where/to/write/resultsfile.csv"))
if err != nil {
log.Fatal("Unable to open output")
}
w := csv.NewWriter(csvOut)
defer csvOut.Close()
// handle header
rec, err := r.Read()
if err != nil {
log.Fatal(err)
}
rec = append(rec, "score")
if err = w.Write(rec); err != nil {
log.Fatal(err)
}
for {
rec, err = r.Read()
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
// get float value
value := rec[1]
floatValue, err := strconv.ParseFloat(value, 64)
if err != nil {
log.Fatal("Record, error: %v, %v", value, err)
}
// calculate scores; THIS EXTERNAL METHOD CANNOT BE CHANGED
score := calculateStuff(floatValue)
scoreString := strconv.FormatFloat(score, 'f', 8, 64)
rec = append(rec, scoreString)
if err = w.Write(rec); err != nil {
log.Fatal(err)
}
w.Flush()
}
}
注意:当然,逻辑都被阻塞在main()中
,最好将其分成几个函数,但这超出了问题的范围。速度“非常慢”有多慢?您正在测试的csv文件的文件大小是多少?因为“慢”一词是主观的……假设计算仅依赖于当前记录(例如,您不需要合计一列的所有值或排序等)不要将整个文件吸入内存,而是在读取时对每个记录进行操作,然后将其写出。当您可以对流进行操作时,请避免使用ioutil.ReadAll
之类的操作。在任何情况下,除非您正在交换(由于使用太多内存)你可能是IO绑定的。另外,请确保检查错误!!例如,如果这是整个程序(即,如果它不是较大程序的一部分),则更明智的做法是不要弄乱文件名,而只是从os.Stdin
读取并写入os.Stdout
,并让shell处理打开的文件(或使用其他程序的直接输出/输入!)。使用bufio.Scanner
和bytes.LastIndex
的更快实现可能看起来像。它比编码/csv
快约3倍,但这牺牲了编码/csv
提供的大量错误检查和灵活性。