在Go中读取CSV文件

时间:2014-07-28 15:50:47

标签: 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 {
        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。

5 个答案:

答案 0 :(得分:13)

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
}

<强> //修改

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

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
    }
}

playground

答案 1 :(得分:12)

我基本上从这里复制了我的答案:https://www.dotnetperls.com/csv-go。对我来说,这是我在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(bufio.NewReader(f))
    for {
        record, err := r.Read()
        // Stop at EOF.
        if err == io.EOF {
            break
        }
        // 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])
        }
    }
}

答案 2 :(得分:3)

Go现在为此提供了一个csv软件包。它是encoding/csv。您可以在这里找到文档:https://golang.org/pkg/encoding/csv/

文档中有两个很好的例子。这是我创建的一种帮助程序方法,用于读取csv文件并返回其记录。

package main

import (
    "encoding/csv"
    "fmt"
    "log"
    "os"
)

func readCsvFile(filePath string) [][]string {
    f, err := os.Open(filePath)
    if err != nil {
        log.Fatal("Unable to read input file " + filePath, err)
    }
    defer f.Close()

    csvReader := csv.NewReader(f)
    records, err := csvReader.ReadAll()
    if err != nil {
        log.Fatal("Unable to parse file as CSV for " + filePath, err)
    }

    return records
}

func main() {
    records := readCsvFile("../tasks.csv")
    fmt.Println(records)
}

答案 3 :(得分:0)

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

读取所有这些CSV文件。

csv文件:

101,300.00,11000901,1155686400
102,250.99,11000902,1432339200

main.go文件:

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

SINGLE FILE:
batch01.csv
[101 300.00 11000901 1155686400]
[102 250.99 11000902 1432339200]

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

  

下面带有Invoice struct

的示例
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()

        go func(file io.Reader) {
            records, _ := csv.NewReader(file).ReadAll()
            for _, row := range records {
                invoice := new(Invoice)
                invoice.InvoiceNumber = row[0]
                invoice.Amount, _ = strconv.ParseFloat(row[1], 64)
                invoice.OrderID, _ = strconv.Atoi(row[2])
                unixTime, _ := strconv.ParseInt(row[3], 10, 64)
                invoice.Date = time.Unix(unixTime, 0)

                fmt.Printf("Received invoice `%v` for $ %.2f \n", invoice.InvoiceNumber, invoice.Amount)
            }
        }(f)

        time.Sleep(10 * time.Millisecond)
    }
}

type Invoice struct {
    InvoiceNumber string
    Amount        float64
    OrderID       int
    Date          time.Time
}

答案 4 :(得分:0)

我也不喜欢默认Reader的冗长,所以我做了一个新的类型 类似于bufio#Scanner

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"))
   }
}

https://golang.org/pkg/encoding/csv