在Go语言中读取CSV文件

huangapple go评论86阅读模式
英文:

Reading CSV file in 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 的噪音。

英文:

Here is a code snippet that reads CSV file:

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
}

Is there a way to improve readability of this code? if and nil noise.

答案1

得分: 79

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("无法读取输入文件 " + filePath, err)
	}
	defer f.Close()

	csvReader := csv.NewReader(f)
	records, err := csvReader.ReadAll()
	if err != nil {
		log.Fatal("无法解析CSV文件 " + filePath, err)
	}

	return records
}

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

希望对你有帮助!

英文:

Go now has a csv package for this. Its is encoding/csv. You can find the docs here: https://golang.org/pkg/encoding/csv/

There are a couple of good examples in the docs. Here is a helper method I created to read a csv file and returns its records.

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

答案2

得分: 35

Go是一种非常冗长的语言,但你可以使用类似以下的代码:

// 预先声明 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() // 这个需要在 err 检查之后

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

    // 在声明中已经定义,不需要 :=
    locations = make(map[string]*Point, len(lines))
    var lat, lon float64 // 预先声明 lat, lon
    for _, line := range lines {
        // 更短、更简洁,而且由于已经声明了 lat 和 err,我们可以这样做。
        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
}

// 编辑

[@Dustin](https://stackoverflow.com/users/39975/dustin) 在 [评论](https://stackoverflow.com/questions/24999079/reading-csv-file-in-go/24999351?noredirect=1#comment38921721_24999351) 中发布了一个更高效和正确的版本,我在这里添加它以保证完整性:

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

[kbd]playground/kbd


<details>
<summary>英文:</summary>
Go is a very verbose language, however you could use something like this:
// 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
] = &amp;Point{lat, lon} } return locations, nil } **//edit** A more efficient and proper version was posted by [@Dustin](https://stackoverflow.com/users/39975/dustin) in the [comments](https://stackoverflow.com/questions/24999079/reading-csv-file-in-go/24999351?noredirect=1#comment38921721_24999351), I&#39;m adding it here for completeness sake: 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 := &amp;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 } } [&lt;kbd&gt;playground&lt;/kbd&gt;](http://play.golang.org/p/3YE2pXSF3x) </details> # 答案3 **得分**: 26 我基本上是从这里复制了我的答案:https://www.dotnetperls.com/csv-go。对我来说,这是一个比我在stackoverflow上找到的答案更好的答案。 ```go import ( "bufio" "encoding/csv" "os" "fmt" "io" ) func ReadCsvFile(filePath string) { // 加载csv文件。 f, _ := os.Open(filePath) // 创建一个新的读取器。 r := csv.NewReader(f) for { record, err := r.Read() // 在EOF处停止。 if err == io.EOF { break } if err != nil { panic(err) } // 显示记录。 // ... 显示记录的长度。 // ... 显示切片的所有单独元素。 fmt.Println(record) fmt.Println(len(record)) for value := range record { fmt.Printf(" %v\n", record[value]) } } }
英文:

I basically copied my answer from here: https://www.dotnetperls.com/csv-go. For me, this was a better answer than what I found on stackoverflow.

import (
&quot;bufio&quot;
&quot;encoding/csv&quot;
&quot;os&quot;
&quot;fmt&quot;
&quot;io&quot;
)
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(&quot;  %v\n&quot;, record[value])
}
}
}

答案4

得分: 5

我也不喜欢默认的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

英文:

I also dislike the verbosity of the default Reader, so I made a new type that is
similar to bufio#Scanner:

package main
import &quot;encoding/csv&quot;
import &quot;io&quot;
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
展开收缩
= 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
展开收缩
] }

Example:

package main
import &quot;strings&quot;
func main() {
s := `Month,Day
January,Sunday
February,Monday`
o := NewScanner(strings.NewReader(s))
for o.Scan() {
println(o.Text(&quot;Month&quot;), o.Text(&quot;Day&quot;))
}
}

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

答案5

得分: -3

您还可以读取目录的内容以加载所有的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("单个文件:")
fmt.Println(file.Name())
filePath := sourcePath + "/" + file.Name()
f, _ := os.Open(filePath)
defer f.Close()
// os.Remove(filePath)
//函数
go func(file io.Reader) {
records, _ := csv.NewReader(file).ReadAll()
for _, row := range records {
fmt.Println(row)
}
}(f)
time.Sleep(10 * time.Millisecond) //给GO routines一些时间来执行
}
}

输出将是:

$ go run main.go

单个文件:
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("单个文件:")
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("收到发票 `%v`,金额为 $ %.2f \n", invoice.InvoiceNumber, invoice.Amount)
}
}(f)
time.Sleep(10 * time.Millisecond)
}
}
type Invoice struct {
InvoiceNumber string
Amount        float64
OrderID       int
Date          time.Time
}
英文:

You can also read contents of a directory to load all the CSV files. And then read all those CSV files 1 by 1 with goroutines

csv file:

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

main.go file:

const sourcePath string = &quot;./source&quot;
func main() {
dir, _ := os.Open(sourcePath)
files, _ := dir.Readdir(-1)
for _, file := range files {
fmt.Println(&quot;SINGLE FILE: &quot;)
fmt.Println(file.Name())
filePath := sourcePath + &quot;/&quot; + 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
}
}

And the OUTPUT will be:

> $ go run main.go

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

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

> Below example with the Invoice struct

func main() {
dir, _ := os.Open(sourcePath)
files, _ := dir.Readdir(-1)
for _, file := range files {
fmt.Println(&quot;SINGLE FILE: &quot;)
fmt.Println(file.Name())
filePath := sourcePath + &quot;/&quot; + 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(&quot;Received invoice `%v` for $ %.2f \n&quot;, invoice.InvoiceNumber, invoice.Amount)
}
}(f)
time.Sleep(10 * time.Millisecond)
}
}
type Invoice struct {
InvoiceNumber string
Amount        float64
OrderID       int
Date          time.Time
}

huangapple
  • 本文由 发表于 2014年7月28日 23:50:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/24999079.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定