現在開發者寫程式,最方便的一點,就是不會的地方,可以問 Google
在 Google 中輸入 Swift big5 to utf8 你會找到許多前人和你遇到一樣的問題。
下面這個聯結就是 Big5 轉 UTF8 的範例文章。
http://supermingblog.blogspot.com/2015/04/ios-big5-to-utf8-or-utf8-to-big5.html
我參考了前人的程式碼,擴充了 String
import Foundation
extension String {
static func dataWtihBig5(data: Data) -> Self? {
let big5Encoding = CFStringEncodings.big5_HKSCS_1999.rawValue
let convertEncodingBig5 = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(big5Encoding))
return String(data: data, encoding: String.Encoding(rawValue: convertEncodingBig5))
}
}
然後在 StockSubscriptionManager 那邊,使用這個方法讓 Data 轉成 String,再餵給 CSVAdapter。
試著印出 csv.header 和其中一個 row,雖然怪怪的,但已經能讀到中文了。
接下來繼續解那個怪怪的 header。
下方是使用 csv 檔的圖,用 Spreadsheet, Excel, Number 打開,可以看到,在 header 上方還有一行,"公開申購公告-抽籤日程表"。就是這一行,讓 CSV 在 parse 的時候沒有得到我們想要的結果。但這個套件沒有從第 n 行開始讀取的 api,那我們就要在 csv string 讀取之前,把第 1 行拿掉。
從第 n 行開始讀取這個職責,我歸類在 CSVAdapter 裡,所以 extension 一個 static func, CSVAdapter 可以得到去除掉第 n 行之前的 string
extension CSVAdapter {
/// 在公開資訊站拿到的 csv 檔中,是有一些檔案的 headers 不是從 line = 0開始,而是 line = 1 或其他行,用這個方以去掉
static func removeLine(_ rawString: String, at line: Int, separator: String = "\n") -> String {
let separatedString = rawString.components(separatedBy: separator)
let removedString = separatedString.suffix(from: line)
let joinedString = removedString.joined(separator: separator)
return joinedString
}
}
接下來,再小輻修改 StockSubscriptionManager 就可以了
//
// StockSubscriptionManager.swift
// ITIronMan
//
// Created by Marvin on 2021/9/4.
//
import Foundation
/// 這個類別專門處理股票申購資訊
class StockSubscriptionManager {
private lazy var alamofireAdapter: AlamofireAdapter = {
return AlamofireAdapter()
}()
func requestStockSubscriptionInfo(year: Int, completion: @escaping (([StockSubscriptionInfo], Error?) -> Void)) {
let urlString = "https://www.twse.com.tw/announcement/publicForm?response=csv&yy=\(year)"
alamofireAdapter.request(urlString, method: .get) { data, response, error in
if let error = error {
completion([], error)
return
}
var subscriptionList = [StockSubscriptionInfo]()
if let data = data,
let string = String.dataWtihBig5(data: data) {
let trimmedString = CSVAdapter.removeLine(string, at: 1)
if let csv = try? CSVAdapter(rawString: trimmedString) {
for each in csv.namedRows {
let stockCode = each["證券代號"] ?? ""
let stockName = each["證券名稱"] ?? ""
let subscriptionStartString = each["申購開始日"] ?? ""
let subscriptionEndString = each["申購結束日"] ?? ""
let subscriptionOccurString = each["抽籤日期"] ?? ""
let price = each["承銷價(元)"] ?? ""
let actualPrice = each["實際承銷價(元)"] ?? ""
let stockDeliveringDateString = each["撥券日期(上市、上櫃日期)"] ?? ""
let stockCountString = each["申購股數"] ?? ""
let totalApplyCountString = each["實際承銷股數"] ?? ""
let subscriptionRateString = each["中籤率(%)"] ?? ""
let subscription = StockSubscriptionInfo(
stockCode: stockCode,
stockName: stockName,
subscriptionStartString: subscriptionStartString,
subscriptionEndString: subscriptionEndString,
subscriptionOccurString: subscriptionOccurString,
price: price,
actualPrice: actualPrice,
stockDeliveringDateString: stockDeliveringDateString,
stockCountString: stockCountString,
totalApplyCountString: totalApplyCountString,
subscriptionRateString: subscriptionRateString)
subscriptionList.append(subscription)
}
}
}
completion(subscriptionList, error)
}
}
}
台股申購 ViewController 所擁有的 model,只要呼叫年份,就完成了 model 的功能,將得到的申購列表回傳 VC 的部分,則會放在下一篇。
//
// StockSubscriptionModel.swift
// ITIronMan
//
// Created by Marvin on 2021/9/4.
//
import Foundation
/// 股票申購 VC 所需的 Model
class StockSubscriptionModel {
private lazy var manager: StockSubscriptionManager = {
return StockSubscriptionManager()
}()
func requestStockSubscription() {
let year = 2021
manager.requestStockSubscriptionInfo(year: year) { subscriptionList, error in
// TODO: - 傳出 list 給 vc
}
}
}