在Go语言中读取CSV文件

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

Reading CSV file in Go

问题

这是一个读取CSV文件的代码片段:

  1. func parseLocation(file string) (map[string]Point, error) {
  2. f, err := os.Open(file)
  3. defer f.Close()
  4. if err != nil {
  5. return nil, err
  6. }
  7. lines, err := csv.NewReader(f).ReadAll()
  8. if err != nil {
  9. return nil, err
  10. }
  11. locations := make(map[string]Point)
  12. for _, line := range lines {
  13. name := line[0]
  14. lat, laterr := strconv.ParseFloat(line[1], 64)
  15. if laterr != nil {
  16. return nil, laterr
  17. }
  18. lon, lonerr := strconv.ParseFloat(line[2], 64)
  19. if lonerr != nil {
  20. return nil, lonerr
  21. }
  22. locations[name] = Point{lat, lon}
  23. }
  24. return locations, nil
  25. }

有没有办法提高这段代码的可读性?减少 if 和 nil 的噪音。

英文:

Here is a code snippet that reads CSV file:

  1. func parseLocation(file string) (map[string]Point, error) {
  2. f, err := os.Open(file)
  3. defer f.Close()
  4. if err != nil {
  5. return nil, err
  6. }
  7. lines, err := csv.NewReader(f).ReadAll()
  8. if err != nil {
  9. return nil, err
  10. }
  11. locations := make(map[string]Point)
  12. for _, line := range lines {
  13. name := line[0]
  14. lat, laterr := strconv.ParseFloat(line[1], 64)
  15. if laterr != nil {
  16. return nil, laterr
  17. }
  18. lon, lonerr := strconv.ParseFloat(line[2], 64)
  19. if lonerr != nil {
  20. return nil, lonerr
  21. }
  22. locations[name] = Point{lat, lon}
  23. }
  24. return locations, nil
  25. }

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文件并返回其记录。

  1. package main
  2. import (
  3. "encoding/csv"
  4. "fmt"
  5. "log"
  6. "os"
  7. )
  8. func readCsvFile(filePath string) [][]string {
  9. f, err := os.Open(filePath)
  10. if err != nil {
  11. log.Fatal("无法读取输入文件 " + filePath, err)
  12. }
  13. defer f.Close()
  14. csvReader := csv.NewReader(f)
  15. records, err := csvReader.ReadAll()
  16. if err != nil {
  17. log.Fatal("无法解析CSV文件 " + filePath, err)
  18. }
  19. return records
  20. }
  21. func main() {
  22. records := readCsvFile("../tasks.csv")
  23. fmt.Println(records)
  24. }

希望对你有帮助!

英文:

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.

  1. package main
  2. import (
  3. "encoding/csv"
  4. "fmt"
  5. "log"
  6. "os"
  7. )
  8. func readCsvFile(filePath string) [][]string {
  9. f, err := os.Open(filePath)
  10. if err != nil {
  11. log.Fatal("Unable to read input file " + filePath, err)
  12. }
  13. defer f.Close()
  14. csvReader := csv.NewReader(f)
  15. records, err := csvReader.ReadAll()
  16. if err != nil {
  17. log.Fatal("Unable to parse file as CSV for " + filePath, err)
  18. }
  19. return records
  20. }
  21. func main() {
  22. records := readCsvFile("../tasks.csv")
  23. fmt.Println(records)
  24. }

答案2

得分: 35

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

  1. // 预先声明 err
  2. func parseLocation(file string) (locations map[string]*Point, err error) {
  3. f, err := os.Open(file)
  4. if err != nil {
  5. return nil, err
  6. }
  7. defer f.Close() // 这个需要在 err 检查之后
  8. lines, err := csv.NewReader(f).ReadAll()
  9. if err != nil {
  10. return nil, err
  11. }
  12. // 在声明中已经定义,不需要 :=
  13. locations = make(map[string]*Point, len(lines))
  14. var lat, lon float64 // 预先声明 lat, lon
  15. for _, line := range lines {
  16. // 更短、更简洁,而且由于已经声明了 lat 和 err,我们可以这样做。
  17. if lat, err = strconv.ParseFloat(line[1], 64); err != nil {
  18. return nil, err
  19. }
  20. if lon, err = strconv.ParseFloat(line[2], 64); err != nil {
  21. return nil, err
  22. }
  23. locations[line[0]] = &Point{lat, lon}
  24. }
  25. return locations, nil
  26. }
  27. // 编辑
  28. [@Dustin](https://stackoverflow.com/users/39975/dustin) 在 [评论](https://stackoverflow.com/questions/24999079/reading-csv-file-in-go/24999351?noredirect=1#comment38921721_24999351) 中发布了一个更高效和正确的版本,我在这里添加它以保证完整性:
  29. ```go
  30. func parseLocation(file string) (map[string]*Point, error) {
  31. f, err := os.Open(file)
  32. if err != nil {
  33. return nil, err
  34. }
  35. defer f.Close()
  36. csvr := csv.NewReader(f)
  37. locations := map[string]*Point{}
  38. for {
  39. row, err := csvr.Read()
  40. if err != nil {
  41. if err == io.EOF {
  42. err = nil
  43. }
  44. return locations, err
  45. }
  46. p := &Point{}
  47. if p.lat, err = strconv.ParseFloat(row[1], 64); err != nil {
  48. return nil, err
  49. }
  50. if p.lon, err = strconv.ParseFloat(row[2], 64); err != nil {
  51. return nil, err
  52. }
  53. locations[row[0]] = p
  54. }
  55. }

[kbd]playground/kbd

  1. <details>
  2. <summary>英文:</summary>
  3. Go is a very verbose language, however you could use something like this:
  4. // predeclare err
  5. func parseLocation(file string) (locations map[string]*Point, err error) {
  6. f, err := os.Open(file)
  7. if err != nil {
  8. return nil, err
  9. }
  10. defer f.Close() // this needs to be after the err check
  11. lines, err := csv.NewReader(f).ReadAll()
  12. if err != nil {
  13. return nil, err
  14. }
  15. //already defined in declaration, no need for :=
  16. locations = make(map[string]*Point, len(lines))
  17. var lat, lon float64 //predeclare lat, lon
  18. for _, line := range lines {
  19. // shorter, cleaner and since we already have lat and err declared, we can do this.
  20. if lat, err = strconv.ParseFloat(line[1], 64); err != nil {
  21. return nil, err
  22. }
  23. if lon, err = strconv.ParseFloat(line[2], 64); err != nil {
  24. return nil, err
  25. }
  26. locations
    ] = &amp;Point{lat, lon}
  27. }
  28. return locations, nil
  29. }
  30. **//edit**
  31. 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:
  32. func parseLocation(file string) (map[string]*Point, error) {
  33. f, err := os.Open(file)
  34. if err != nil {
  35. return nil, err
  36. }
  37. defer f.Close()
  38. csvr := csv.NewReader(f)
  39. locations := map[string]*Point{}
  40. for {
  41. row, err := csvr.Read()
  42. if err != nil {
  43. if err == io.EOF {
  44. err = nil
  45. }
  46. return locations, err
  47. }
  48. p := &amp;Point{}
  49. if p.lat, err = strconv.ParseFloat(row[1], 64); err != nil {
  50. return nil, err
  51. }
  52. if p.lon, err = strconv.ParseFloat(row[2], 64); err != nil {
  53. return nil, err
  54. }
  55. locations[row[0]] = p
  56. }
  57. }
  58. [&lt;kbd&gt;playground&lt;/kbd&gt;](http://play.golang.org/p/3YE2pXSF3x)
  59. </details>
  60. # 答案3
  61. **得分**: 26
  62. 我基本上是从这里复制了我的答案:https://www.dotnetperls.com/csv-go。对我来说,这是一个比我在stackoverflow上找到的答案更好的答案。
  63. ```go
  64. import (
  65. "bufio"
  66. "encoding/csv"
  67. "os"
  68. "fmt"
  69. "io"
  70. )
  71. func ReadCsvFile(filePath string) {
  72. // 加载csv文件。
  73. f, _ := os.Open(filePath)
  74. // 创建一个新的读取器。
  75. r := csv.NewReader(f)
  76. for {
  77. record, err := r.Read()
  78. // 在EOF处停止。
  79. if err == io.EOF {
  80. break
  81. }
  82. if err != nil {
  83. panic(err)
  84. }
  85. // 显示记录。
  86. // ... 显示记录的长度。
  87. // ... 显示切片的所有单独元素。
  88. fmt.Println(record)
  89. fmt.Println(len(record))
  90. for value := range record {
  91. fmt.Printf(" %v\n", record[value])
  92. }
  93. }
  94. }
英文:

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.

  1. import (
  2. &quot;bufio&quot;
  3. &quot;encoding/csv&quot;
  4. &quot;os&quot;
  5. &quot;fmt&quot;
  6. &quot;io&quot;
  7. )
  8. func ReadCsvFile(filePath string) {
  9. // Load a csv file.
  10. f, _ := os.Open(filePath)
  11. // Create a new reader.
  12. r := csv.NewReader(f)
  13. for {
  14. record, err := r.Read()
  15. // Stop at EOF.
  16. if err == io.EOF {
  17. break
  18. }
  19. if err != nil {
  20. panic(err)
  21. }
  22. // Display record.
  23. // ... Display record length.
  24. // ... Display all individual elements of the slice.
  25. fmt.Println(record)
  26. fmt.Println(len(record))
  27. for value := range record {
  28. fmt.Printf(&quot; %v\n&quot;, record[value])
  29. }
  30. }
  31. }

答案4

得分: 5

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

  1. package main
  2. import "encoding/csv"
  3. import "io"
  4. type Scanner struct {
  5. Reader *csv.Reader
  6. Head map[string]int
  7. Row []string
  8. }
  9. func NewScanner(o io.Reader) Scanner {
  10. csv_o := csv.NewReader(o)
  11. a, e := csv_o.Read()
  12. if e != nil {
  13. return Scanner{}
  14. }
  15. m := map[string]int{}
  16. for n, s := range a {
  17. m[s] = n
  18. }
  19. return Scanner{Reader: csv_o, Head: m}
  20. }
  21. func (o *Scanner) Scan() bool {
  22. a, e := o.Reader.Read()
  23. o.Row = a
  24. return e == nil
  25. }
  26. func (o Scanner) Text(s string) string {
  27. return o.Row[o.Head[s]]
  28. }

示例:

  1. package main
  2. import "strings"
  3. func main() {
  4. s := `Month,Day
  5. January,Sunday
  6. February,Monday`
  7. o := NewScanner(strings.NewReader(s))
  8. for o.Scan() {
  9. println(o.Text("Month"), o.Text("Day"))
  10. }
  11. }

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:

  1. package main
  2. import &quot;encoding/csv&quot;
  3. import &quot;io&quot;
  4. type Scanner struct {
  5. Reader *csv.Reader
  6. Head map[string]int
  7. Row []string
  8. }
  9. func NewScanner(o io.Reader) Scanner {
  10. csv_o := csv.NewReader(o)
  11. a, e := csv_o.Read()
  12. if e != nil {
  13. return Scanner{}
  14. }
  15. m := map[string]int{}
  16. for n, s := range a {
  17. m
    展开收缩
    = n
  18. }
  19. return Scanner{Reader: csv_o, Head: m}
  20. }
  21. func (o *Scanner) Scan() bool {
  22. a, e := o.Reader.Read()
  23. o.Row = a
  24. return e == nil
  25. }
  26. func (o Scanner) Text(s string) string {
  27. return o.Row[o.Head
    展开收缩
    ]
  28. }

Example:

  1. package main
  2. import &quot;strings&quot;
  3. func main() {
  4. s := `Month,Day
  5. January,Sunday
  6. February,Monday`
  7. o := NewScanner(strings.NewReader(s))
  8. for o.Scan() {
  9. println(o.Text(&quot;Month&quot;), o.Text(&quot;Day&quot;))
  10. }
  11. }

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

答案5

得分: -3

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

csv文件:

  1. 101,300.00,11000901,1155686400
  2. 102,250.99,11000902,1432339200

main.go文件:

  1. const sourcePath string = "./source"
  2. func main() {
  3. dir, _ := os.Open(sourcePath)
  4. files, _ := dir.Readdir(-1)
  5. for _, file := range files {
  6. fmt.Println("单个文件:")
  7. fmt.Println(file.Name())
  8. filePath := sourcePath + "/" + file.Name()
  9. f, _ := os.Open(filePath)
  10. defer f.Close()
  11. // os.Remove(filePath)
  12. //函数
  13. go func(file io.Reader) {
  14. records, _ := csv.NewReader(file).ReadAll()
  15. for _, row := range records {
  16. fmt.Println(row)
  17. }
  18. }(f)
  19. time.Sleep(10 * time.Millisecond) //给GO routines一些时间来执行
  20. }
  21. }

输出将是:

$ go run main.go

  1. 单个文件:
  2. batch01.csv
  3. [101 300.00 11000901 1155686400]
  4. [102 250.99 11000902 1432339200]

下面是使用Invoice struct的示例:

  1. func main() {
  2. dir, _ := os.Open(sourcePath)
  3. files, _ := dir.Readdir(-1)
  4. for _, file := range files {
  5. fmt.Println("单个文件:")
  6. fmt.Println(file.Name())
  7. filePath := sourcePath + "/" + file.Name()
  8. f, _ := os.Open(filePath)
  9. defer f.Close()
  10. go func(file io.Reader) {
  11. records, _ := csv.NewReader(file).ReadAll()
  12. for _, row := range records {
  13. invoice := new(Invoice)
  14. invoice.InvoiceNumber = row[0]
  15. invoice.Amount, _ = strconv.ParseFloat(row[1], 64)
  16. invoice.OrderID, _ = strconv.Atoi(row[2])
  17. unixTime, _ := strconv.ParseInt(row[3], 10, 64)
  18. invoice.Date = time.Unix(unixTime, 0)
  19. fmt.Printf("收到发票 `%v`,金额为 $ %.2f \n", invoice.InvoiceNumber, invoice.Amount)
  20. }
  21. }(f)
  22. time.Sleep(10 * time.Millisecond)
  23. }
  24. }
  25. type Invoice struct {
  26. InvoiceNumber string
  27. Amount float64
  28. OrderID int
  29. Date time.Time
  30. }
英文:

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:

  1. 101,300.00,11000901,1155686400
  2. 102,250.99,11000902,1432339200

main.go file:

  1. const sourcePath string = &quot;./source&quot;
  2. func main() {
  3. dir, _ := os.Open(sourcePath)
  4. files, _ := dir.Readdir(-1)
  5. for _, file := range files {
  6. fmt.Println(&quot;SINGLE FILE: &quot;)
  7. fmt.Println(file.Name())
  8. filePath := sourcePath + &quot;/&quot; + file.Name()
  9. f, _ := os.Open(filePath)
  10. defer f.Close()
  11. // os.Remove(filePath)
  12. //func
  13. go func(file io.Reader) {
  14. records, _ := csv.NewReader(file).ReadAll()
  15. for _, row := range records {
  16. fmt.Println(row)
  17. }
  18. }(f)
  19. time.Sleep(10 * time.Millisecond)// give some time to GO routines for execute
  20. }
  21. }

And the OUTPUT will be:

> $ go run main.go

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

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

> Below example with the Invoice struct

  1. func main() {
  2. dir, _ := os.Open(sourcePath)
  3. files, _ := dir.Readdir(-1)
  4. for _, file := range files {
  5. fmt.Println(&quot;SINGLE FILE: &quot;)
  6. fmt.Println(file.Name())
  7. filePath := sourcePath + &quot;/&quot; + file.Name()
  8. f, _ := os.Open(filePath)
  9. defer f.Close()
  10. go func(file io.Reader) {
  11. records, _ := csv.NewReader(file).ReadAll()
  12. for _, row := range records {
  13. invoice := new(Invoice)
  14. invoice.InvoiceNumber = row[0]
  15. invoice.Amount, _ = strconv.ParseFloat(row[1], 64)
  16. invoice.OrderID, _ = strconv.Atoi(row[2])
  17. unixTime, _ := strconv.ParseInt(row[3], 10, 64)
  18. invoice.Date = time.Unix(unixTime, 0)
  19. fmt.Printf(&quot;Received invoice `%v` for $ %.2f \n&quot;, invoice.InvoiceNumber, invoice.Amount)
  20. }
  21. }(f)
  22. time.Sleep(10 * time.Millisecond)
  23. }
  24. }
  25. type Invoice struct {
  26. InvoiceNumber string
  27. Amount float64
  28. OrderID int
  29. Date time.Time
  30. }

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:

确定