在Go中读取CSV文件

在Go中读取CSV文件,go,Go,我在学围棋。以下是读取CSV文件的代码段: func parseLocation(file string) (map[string]Point, error) { f, err := os.Open(file) defer f.Close() if err != nil { return nil, err } lines, err := csv.NewReader(f).ReadAll() if err != nil {

我在学围棋。以下是读取CSV文件的代码段:

func parseLocation(file string) (map[string]Point, error) {
    f, err := os.Open(file)
    defer f.Close()
    if err != nil {
        return nil, err
    }
    lines, err := csv.NewReader(f).ReadAll()
    if err != nil {
        return nil, err
    }
    locations := make(map[string]Point)
    for _, line := range lines {
        name := line[0]
        lat, laterr := strconv.ParseFloat(line[1], 64)
        if laterr != nil {
            return nil, laterr
        }
        lon, lonerr := strconv.ParseFloat(line[2], 64)
        if lonerr != nil {
            return nil, lonerr
        }
        locations[name] = Point{lat, lon}
    }
    return locations, nil
}

有没有办法提高这段代码的可读性?if和nil noise。

Go是一种非常冗长的语言,但是您可以使用如下内容:

// predeclare err
func parseLocation(file string) (locations map[string]*Point, err error) {
    f, err := os.Open(file)
    if err != nil {
        return nil, err
    }
    defer f.Close() // this needs to be after the err check

    lines, err := csv.NewReader(f).ReadAll()
    if err != nil {
        return nil, err
    }

    //already defined in declaration, no need for :=
    locations = make(map[string]*Point, len(lines))
    var lat, lon float64 //predeclare lat, lon
    for _, line := range lines {
        // shorter, cleaner and since we already have lat and err declared, we can do this.
        if lat, err = strconv.ParseFloat(line[1], 64); err != nil {
            return nil, err
        }
        if lon, err = strconv.ParseFloat(line[2], 64); err != nil {
            return nil, err
        }
        locations[line[0]] = &Point{lat, lon}
    }
    return locations, nil
}
//编辑

在中发布了一个更有效、更合适的版本,为了完整起见,我在这里添加它:

func parseLocation(file string) (map[string]*Point, error) {
    f, err := os.Open(file)
    if err != nil {
        return nil, err
    }
    defer f.Close()

    csvr := csv.NewReader(f)

    locations := map[string]*Point{}
    for {
        row, err := csvr.Read()
        if err != nil {
            if err == io.EOF {
                err = nil
            }
            return locations, err
        }

        p := &Point{}
        if p.lat, err = strconv.ParseFloat(row[1], 64); err != nil {
            return nil, err
        }
        if p.lon, err = strconv.ParseFloat(row[2], 64); err != nil {
            return nil, err
        }
        locations[row[0]] = p
    }
}

我基本上是从这里复制了我的答案:。对我来说,这是一个比我在stackoverflow上找到的更好的答案

import (
    "bufio"
    "encoding/csv"
    "os"
    "fmt"
    "io"
)

func ReadCsvFile(filePath string)  {
    // Load a csv file.
    f, _ := os.Open(filePath)

    // Create a new reader.
    r := csv.NewReader(f)
    for {
        record, err := r.Read()
        // Stop at EOF.
        if err == io.EOF {
            break
        }

        if err != nil {
            panic(err)
        }
        // Display record.
        // ... Display record length.
        // ... Display all individual elements of the slice.
        fmt.Println(record)
        fmt.Println(len(record))
        for value := range record {
            fmt.Printf("  %v\n", record[value])
        }
    }
}

您还可以读取目录的内容以加载所有CSV文件。然后使用
goroutines

csv
文件:

101,300.00,11000901,1155686400
102,250.99,11000902,1432339200
const sourcePath string = "./source"

func main() {
    dir, _ := os.Open(sourcePath)
    files, _ := dir.Readdir(-1)

    for _, file := range files {
        fmt.Println("SINGLE FILE: ")
        fmt.Println(file.Name())
        filePath := sourcePath + "/" + file.Name()
        f, _ := os.Open(filePath)
        defer f.Close()
        // os.Remove(filePath)

        //func
        go func(file io.Reader) {
            records, _ := csv.NewReader(file).ReadAll()
            for _, row := range records {
                fmt.Println(row)
            }
        }(f)

        time.Sleep(10 * time.Millisecond)// give some time to GO routines for execute
    }
}
main.go
文件:

101,300.00,11000901,1155686400
102,250.99,11000902,1432339200
const sourcePath string = "./source"

func main() {
    dir, _ := os.Open(sourcePath)
    files, _ := dir.Readdir(-1)

    for _, file := range files {
        fmt.Println("SINGLE FILE: ")
        fmt.Println(file.Name())
        filePath := sourcePath + "/" + file.Name()
        f, _ := os.Open(filePath)
        defer f.Close()
        // os.Remove(filePath)

        //func
        go func(file io.Reader) {
            records, _ := csv.NewReader(file).ReadAll()
            for _, row := range records {
                fmt.Println(row)
            }
        }(f)

        time.Sleep(10 * time.Millisecond)// give some time to GO routines for execute
    }
}
输出将是:

$go运行main.go

----------------------------------------------------------- -----------------------------------

下面是带有
发票结构的示例


Go now为此提供了一个csv包。它是
编码/csv
。您可以在此处找到文档:

文档中有几个很好的例子。下面是我创建的一个助手方法,用于读取csv文件并返回其记录

主程序包
进口(
“编码/csv”
“fmt”
“日志”
“操作系统”
)
func readCsvFile(文件路径字符串)[]字符串{
f、 错误:=os.Open(文件路径)
如果错误!=零{
log.Fatal(“无法读取输入文件”+文件路径,错误)
}
延迟f.关闭()
csvReader:=csv.NewReader(f)
记录,错误:=csvReader.ReadAll()
如果错误!=零{
log.Fatal(“无法将“+文件路径”的文件解析为CSV,错误)
}
退货记录
}
func main(){
记录:=readCsvFile(../tasks.csv)
fmt.Println(记录)
}

我也不喜欢默认阅读器的冗长,所以我创建了一个新类型 与bufio扫描仪类似:

package main
import "encoding/csv"
import "io"

type Scanner struct {
   Reader *csv.Reader
   Head map[string]int
   Row []string
}

func NewScanner(o io.Reader) Scanner {
   csv_o := csv.NewReader(o)
   a, e := csv_o.Read()
   if e != nil {
      return Scanner{}
   }
   m := map[string]int{}
   for n, s := range a {
      m[s] = n
   }
   return Scanner{Reader: csv_o, Head: m}
}

func (o *Scanner) Scan() bool {
   a, e := o.Reader.Read()
   o.Row = a
   return e == nil
}

func (o Scanner) Text(s string) string {
   return o.Row[o.Head[s]]
}
例如:

package main
import "strings"

func main() {
   s := `Month,Day
January,Sunday
February,Monday`

   o := NewScanner(strings.NewReader(s))
   for o.Scan() {
      println(o.Text("Month"), o.Text("Day"))
   }
}

if err!=nil
:如果此时出现错误,这是因为文件无法打开,因此,您不能对其使用
Close
。@julienc在错误检查后不需要使用DEREF语句。您当然可以关闭未打开的文件:我建议不要命名这些返回变量。你甚至不用它们的名字,这只会引起额外的混乱。在解析之前,一次将整个内容读入内存是不必要的。我们应该避免这样做。lat/long predeclaration的范围太大,但实际上没有必要。我会这样做:你应该把这个作为一个答案,你得到了我的支持。我考虑过,但它几乎是你的答案,只是有一些小的改变。我最担心的是内存消耗,但你是从原始版本中取出来的。@其中一个为什么你会说go是“一种非常冗长的语言”@user3666882因为它是,没有像大多数其他语言一样的“快捷方式”,我的意思是甚至没有trinity运算符。
对于u,value:=range reader{fmt.Printf(“%v\n”,value)}
也可以