CSVImporter
轻松逐行导入 CSV 文件。
理由
“为什么还需要另一个 CSVImporter?”你可能问。“已经有 SwiftCSV 和 CSwiftV 了,”你可能会说。事实是,这些框架对 较小的 CSV 文件 工作得很好。但是一旦你有一个真正 大的 CSV 文件(或者 可能 会这样,因为你可以允许用户导入他们想要的任何 CSV 文件),那么这些解决方案可能会给一些用户带来 延迟和内存问题。
另一方面,CSVImporter 同时支持 异步操作(防止延迟)并且是逐行读取您的 CSV 文件(防止内存问题)。除此之外,它使用简单,并提供漂亮的回调,以指示失败、进度、完成,甚至如果你希望的话,还可以进行 数据处理。
安装
目前推荐通过在 macOS 上使用 Carthage 或在 Linux 上使用 Swift Package Manager 来安装此库。《CocoaPods》可能也可以工作,但尚未经过测试。
当然,您也可以通过下载或使用 git 子模块将此框架手动包含到项目中。
使用方法
请查看 UsageExamples.playground 和 Tests/CSVImporterTests/CSVImporterSpec.swift 文件以获取提供的完整功能列表。要运行它,请从 .xcworkspace
内打开 Playground。
基本 CSV 导入
首先创建一个 CSVImporter 实例,并指定 CSV 行内数据的类型。默认数据类型是 String
对象的数组,看起来像这样
let path = "path/to/your/CSV/file"
let importer = CSVImporter<[String]>(path: path)
importer.startImportingRecords { $0 }.onFinish { importedRecords in
for record in importedRecords {
// record is of type [String] and contains all data in a line
}
}
注意,在创建 CSVImporter
对象时,您可以在路径旁指定一个 **替代分隔符**。如果没有指定,则默认为 ,
。
异步与回调
CSVImporter 默认使用异步工作,因此不会阻塞主线程。如您所见,当完成导入后,将调用 onFinish
方法使用结果。还有针对失败情况的 onFail
(例如,当给定的路径不包含 CSV 文件时),onProgress
会定期调用并提供已处理的行数(例如,用于进度指示器)。您可以像以下这样连锁调用它们
importer.startImportingRecords { $0 }.onFail {
print("The CSV file couldn't be read.")
}.onProgress { importedDataLinesCount in
print("\(importedDataLinesCount) lines were already imported.")
}.onFinish { importedRecords in
print("Did finish import with \(importedRecords.count) records.")
}
默认情况下,实际导入工作在 .utility
全球后台队列中完成,回调在 main
队列中调用。这样,繁重的工作是异步完成的,但回调允许您更新界面。如果您需要不同的行为,您可以在创建 CSVImporter 对象时自定义队列,如下所示
let path = "path/to/your/CSV/file"
let importer = CSVImporter<[String]>(path: path, workQosClass: .background, callbacksQosClass: .utility)
异步导入
如果知道文件足够小或者阻塞UI不是问题,也可以使用同步导入方法来导入数据。只需调用 importRecords
而不是 startImportingRecords
,你将直接收到最终结果(与使用 startImportingRecords
时在 onFinish
闭包中相同的 uygulama内内容)。
let importedRecords = importer.importRecords { $0 }
注意,此方法没有任何选项可以通知进度或失败 - 你只得到结果。检查结果数组是否为空以识别潜在失败。
简单的数据映射
如上所述,默认类型为 [String]
,但您可以提供所需的任何类型。例如,假设您有一个这样的类
class Student {
let firstName: String, lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
而且您的CSV文件看起来像下面这样
Harry,Potter
Hermione,Granger
Ron,Weasley
然后您可以将映射器指定为闭包而不是上面的例子中的 { $0 }
,如下所示
let path = "path/to/Hogwarts/students"
let importer = CSVImporter<Student>(path: path)
importer.startImportingRecords { recordValues -> Student in
return Student(firstName: recordValues[0], lastName: recordValues[1])
}.onFinish { importedRecords in
for student in importedRecords {
// Now importedRecords is an array of Students
}
}
头结构支持
最后但并非最不重要的是,一些CSV文件的数据结构在第一行中指定,如下所示
firstName,lastName
Harry,Potter
Hermione,Granger
Ron,Weasley
在这种情况下,CSV导入器可以自动将每个记录作为一个字典提供,如下所示
let path = "path/to/Hogwarts/students"
let importer = CSVImporter<[String: String]>(path: path)
importer.startImportingRecords(structure: { (headerValues) -> Void in
print(headerValues) // => ["firstName", "lastName"]
}) { $0 }.onFinish { importedRecords in
for record in importedRecords {
print(record) // => e.g. ["firstName": "Harry", "lastName": "Potter"]
print(record["firstName"]) // prints "Harry" on first, "Hermione" on second run
print(record["lastName"]) // prints "Potter" on first, "Granger" on second run
}
}
注意:如果记录值计数与第一行值计数不匹配,则记录将被忽略。
贡献
请参阅文件 CONTRIBUTING.md。
许可证
本库采用MIT 许可证发布。详见 LICENSE 文件获取详细信息。