Swift簡易讀取CSV檔案
讀取外部檔案的方法每個語言都會有,而CSV檔是用固定的格式把純文字文件變得和試算表一樣的表格。跟純文字比起來是能轉化成專案中的Model Class做進一步使用,但又不像JSON已經有一套標準函式庫能夠直接套用。因此需要自己去撰寫。
基本資訊
- Parsing CSV file in Swift
- XCode 26.6
- Swift5
記得我是截取某個人的回答,在這邊重新整理一遍
製作流程
1.準備來源CSV檔案
雖然這算是廢話,但是有了資料才能夠針對這份資料進行處理,就和處理JSON一樣,要了解文檔格式才能進行解析。
這邊就有一個示意用的CSV內容,在這邊命名為data.csv:
Firstname,Last name,Age,Registered
Duncan,Campbell,40,True
Tobi,Dorner,36,False
Saskia,Boogarts,29,True
如果想要自己練習,又不知道該上哪找資料,Mockaroo或許是個好幫手,能產生出一批假資料。
2.以這個CSV檔的內容建立一個Model
基本上就是將欄位項目轉成Model的方式:
struct Person {
var firstName: String
var lastName: String
var age: Int
var isRegistered: Bool
}
3.宣告這個Model陣列
因為這些結構是單一一筆的資料,要把CSV檔的所有資料都解析出來,就要像這樣:
var people = [Person]()
4.讀取檔案
這邊就是Swift常用的讀取外部資源方法,以範例data.csv就會是這樣:
guard let filepath = Bundle.main.path(forResource: "data", ofType: "csv") else {
return
}
Bundle.main就默認當下專案APP的主要位置,如果是寫Framework等插件工具的話,需要轉換思路去想要從哪邊取得,否則會因為默認位置而取不到位在Framework的資源檔案。
5.把檔案轉成String
因為CSV本質是純文字檔,用String去接:
var data = ""
do {
data = try String(contentsOfFile: filepath)
} catch {
print(error)
return
}
6.把每一行都分開成單獨的陣列字串
因為我們需要一行一行拆解內容,讓一整包的文字檔才能進行下一步處理:
let rows = data.components(separatedBy: "\n")
7.移除欄位的標題列
標題列主要是給人看,在t程式運作上不需要納入:
rows.removeFirst()
8.用迴圈拆解每列的內容並將內容新增並存入陣列
拆解並新增的完整迴圈大致上長這樣:
for row in rows {
let columns = row.components(separatedBy: ",")//切出欄位陣列
//將陣列取出各個欄位
let firstName = columns[0]
let lastName = columns[1]
let age = Int(columns[2]) ?? 0
let isRegistered = columns[3] == "True"
//新增Model並把值代入
let person = Person(firstName: firstName, lastName: lastName, age: age, isRegistered: isRegistered)
people.append(person)//加入陣列
}
迴圈的每一行就是整個Person的個欄位內容,但是原始文字資料(ex:Duncan,Campbell,40,True)還需要根據欄位做分割成columns才能夠進行,正是.components(separatedBy: ",")這邊的用意。
通常會有兩種做法,其中一種就是將欄位一個個取出,並塞入新增的Model;另一個則是直接在新增Model時就帶入每個欄位。結果都一樣但是閱讀上就有不同的感覺,依照習慣做就好,沒有對錯。
完整程式碼
//MARK: - Model
struct Person {
var firstName: String
var lastName: String
var age: Int
var isRegistered: Bool
}
//MARK: - value
var people = [Person]()
//MARK: - function
func convertCSVIntoArray() {
//讀取資源data.csv
guard let filepath = Bundle.main.path(forResource: "data", ofType: "csv") else {
return
}
//將filepath轉換成String
var data = ""
do {
data = try String(contentsOfFile: filepath)
} catch {
print(error)
return
}
//分割(split)出每一行(rows)的資料,行分隔常用的是"\n"
var rows = data.components(separatedBy: "\n")
//移除標題欄位
rows.removeFirst()
//每行資料拆解並加入`people`陣列
for row in rows {
let columns = row.components(separatedBy: ",")//列的分隔記號要看原始檔,常用的有`,`、`|`
//防呆:檢查欄位總數有無超出預期
//大於預期數量在建立上沒有程式錯誤,但是有可能因為是填錯資料導致這筆資料不可信
//小於預期則會發生out of range的閃退
if columns.count == 4 {
let firstName = columns[0]
let lastName = columns[1]
let age = Int(columns[2]) ?? 0
let isRegistered = columns[3] == "True"
let person = Person(firstName: firstName, lastName: lastName, age: age, isRegistered: isRegistered)
people.append(person)
}
}
}
總結
這也不是什麼困難的功能,只是久沒用就多少會忘記。