WEBサイト制作・アプリ開発・システム開発・ブランディングデザイン制作に関するご相談はお気軽にご連絡ください。
構想段階からじっくりとヒアリングし、お客様の課題にあわせたアプローチ手法でお客様の“欲しかった”をカタチにしてご提案いたします。
Blog スタッフブログ
iOS
Swift
システム開発
ひとくちコードスニペット
[Swift]ローカルのCSVファイルを読み込むコードスニペット

こんにちは、株式会社MIXシステム開発担当のBloomです。
今回はSwiftでローカルのCSVファイルを読み込みたい時に利用するコードスニペットを掲載させていただきます。
それではさっそくCSVファイルを読み込むクラスを実装しましょう。
class CSVLoader {
/// リソース内 CSV を [[String]] で返す
static func load(name: String, in bundle: Bundle = .main) throws -> [[String]] {
guard let url = bundle.url(forResource: name, withExtension: "csv") else {
throw NSError(domain: "file not found", code: -1)
}
let data = try Data(contentsOf: url)
// 1. 文字コード推定
guard let encoding = data.detectStringEncoding() else {
throw NSError(domain: "encoding detect failed", code: -2)
}
// 2. デコードおよび改行コード置換
guard var text = String(data: data, encoding: encoding) else {
throw NSError(domain: "decode failed", code: -3)
}
text.normalizeNewlinesInPlace() // CRLF/CRをLFへ置換
// 3. CSVパース処理
return parseCSV(text)
}
}
private extension Data {
// 簡単な文字コード推定処理
func detectStringEncoding() -> String.Encoding? {
// 1.BOMで判定
if self.starts(with: [0xEF, 0xBB, 0xBF]) { // UTF-8 BOM
return .utf8
}
if self.starts(with: [0xFF, 0xFE]) { // UTF-16 LE BOM
return .utf16LittleEndian
}
if self.starts(with: [0xFE, 0xFF]) { // UTF-16 BE BOM
return .utf16BigEndian
}
// 2.BOMなしutf8判定
if String(data: self, encoding: .utf8) != nil {
return .utf8
}
// 3.sjis判定
if String(data: self, encoding: .shiftJIS) != nil {
return .shiftJIS
}
return nil
}
}
private extension String {
mutating func normalizeNewlinesInPlace() {
self = self.replacingOccurrences(of: "\r\n", with: "\n")
self = self.replacingOccurrences(of: "\r", with: "\n")
}
}
private func parseCSV(_ text: String, separator: Character = ",", quote: Character = "\"") -> [[String]] {
var rows: [[String]] = []
var row: [String] = []
var field = ""
var inQuotes = false
var iterator = text.makeIterator()
func endField() {
row.append(field)
field = ""
}
func endRow() {
endField()
rows.append(row)
row.removeAll(keepingCapacity: true)
}
while let c = iterator.next() {
if inQuotes {
if c == quote {
if let next = iterator.peek(), next == quote {
_ = iterator.next()
field.append(quote)
} else {
inQuotes = false
}
} else {
field.append(c)
}
} else {
switch c {
case quote:
inQuotes = true
case separator:
endField()
case "\n":
endRow()
default:
field.append(c)
}
}
}
if !(field.isEmpty && row.isEmpty) {
endRow()
}
return rows
}
private extension String.Iterator {
mutating func peek() -> Character? {
var copy = self
return copy.next()
}
}
呼び出し例
do {
let rows = try CSVLoader.load(name: "sample") // sample.csv
print(rows)
} catch {
print("CSV 読み込み失敗: \(error)")
}
ここではCSVファイルをある程度文字コードの自動判定、改行コードの自動判定を行いながら読み込んでいます。ユーザがインポートするCSVファイルであればある程度自動判定するのが良いかと思われますが、リソース内CSVファイルであればこれらは直接指定する方が良いため適宜書き換えて実装をしてください。
これだけでCSVファイルの読み込みができました。良かったですね。