package
0.0.0-20240927143213-b11bb4fab650
Repository: https://github.com/jocelynh1110/go-practice.git
Documentation: pkg.go.dev
# README
12-7 處理 CSV 格式檔案
除了純文字檔和 JSON 資料外,程式最常存取的檔案格式之一:CSV(comma-separated value, 逗號分隔值)。
CSV 本身是純文字,但使用逗號來分隔每一直行(column)或欄位的值,而每一橫列(row)則以每一行結尾的換行符號區隔。
以下為經典 CSV 格式資料:
firstName,lastName,age
Celia,Jones,30
Case,Jo,25
第一行是標頭(header),即各行或欄位的名稱。 csv 的分隔符號是半形逗號,有時也可能用空格或 tab(\t)等字元。
12-7-1 走訪 CSV 檔內容
Go 語言也提供了標準函式庫 encoding/csv,可用來解析 CSV 格式資料:
func NewReader(r io.Reader) *Reader
bufio 套件一樣,csv 套件的 NewReader() 接收一個 io.Reader 介面型別,後傳回 csv.Reader 結構。
此結構的 Read() 方法能用來讀取 csv 資料的一行內容,並將其轉換成 []string 切片:
func (r *Reader) Read() (record []string, err error)
不同於 bufio.Reader 的 ReadString() 方法,csv 套件會自動判斷結尾的換行符號。但這行文字會以字串切片的形式傳回。
例、從一個名為 data.csv 的檔案讀取 CSV 資料,data.csv 檔案內容寫入,如下:
package main
import "os"
func main() {
f, err := os.OpenFile("data.csv", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer f.Close()
f.WriteString("firstName,lastName,age\nCelina,Joned,18\nCailyn,Hely,28\nCayden,Smith,42")
}
只要開啟上面這個 csv 檔案,就能將 os.File 結構傳給 csv.NewReader() 來建立所需的物件:
package main
import (
"encoding/csv"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("data.csv") // 開啟 CSV 檔案
if err != nil {
panic(err)
}
defer file.Close()
reader := csv.NewReader(file) // 取得 csv.Reader 結構
for {
record, err := reader.Read() // 從 csv.Reader 讀取一行資料
if err == io.EOF { // 遇到檔案結尾錯誤,就離開迴圈
break
}
if err != nil {
fmt.Println(err)
continue
}
fmt.Println(record) // 印出該行資料
}
}
顯示結果:
[firstName lastName age]
[Celina Joned 18]
[Cailyn Hely 28]
[Cayden Smith 42]
12-7-2 讀取每行資料各欄位的值
如前所提,csv.Reader 的 Read() 方法會傳回 []string 切片。 csv 套件會以半形逗號為依據,將各欄位轉成字串切片的不同元素:索引 0 是左邊數來第一個欄位,索引 1 是第二個...。
所以只要事先知道 CSV 檔的欄位組成,就很容易取出想要的東西。
索引 0 | 索引 1 | 索引 2 |
---|---|---|
firstName | lastName | age |
Celina | Jones | 20 |
跳過標頭的話,可以加入一個布林變數做為開關,在 for 迴圈第一次執行時選擇不印出東西。
例、修改上節範例,跳過標頭:
package main
import (
"encoding/csv"
"fmt"
"io"
"os"
)
const (
firstName = iota // CSV 欄位索引
lastName
age
)
func main() {
file, err := os.Open("data.csv") // 開啟 CSV 檔案
if err != nil {
panic(err)
}
defer file.Close()
header := true // 標頭開關
reader := csv.NewReader(file) // 取得 csv.Reader 結構
for {
record, err := reader.Read() // 從 csv.Reader 讀取一行資料
if err == io.EOF { // 遇到檔案結尾錯誤,就離開迴圈
break
}
if err != nil {
fmt.Println(err)
continue
}
if header {
header = false
continue // 跳過第一行(標頭)
}
fmt.Println("--------------")
fmt.Println("First name:", record[firstName])
fmt.Println("Last name:", record[lastName])
fmt.Println("Age:", record[age])
}
}
顯示結果:
--------------
First name: Celina
Last name: Joned
Age: 18
--------------
First name: Cailyn
Last name: Hely
Age: 28
--------------
First name: Cayden
Last name: Smith
Age: 42