在Go语言中读取CSV文件

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

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:

确定