轻松逐行导入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 才能使其正常工作。
首先创建一个 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
队列中调用。 这样,繁重的工作以异步方式完成,但回调允许你更新你的 UI。 如果你需要不同的行为,你可以在创建 CSVImporter 对象时自定义队列,如下所示
let path = "path/to/your/CSV/file"
let importer = CSVImporter<[String]>(path: path, workQosClass: .background, callbacksQosClass: .utility)
如果你知道你的文件足够小,或者阻塞 UI 没有问题,你也可以使用同步导入方法来导入你的数据。 只需调用 importRecords
而不是 startImportingRecords
,你将直接收到最终结果(与使用 startImportingRecords
时 onFinish
闭包中的内容相同)
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
在这种情况下,CSVImporter 可以自动将每个记录作为字典提供,如下所示
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
}
}
注意:如果记录的值计数与第一行的值计数不匹配,则该记录将被忽略。
BartyCrouch 由 Cihat Gündüz 在业余时间为你带来。 如果你想感谢我并支持这个项目的开发,请在 PayPal 上进行小额捐赠。 如果你也喜欢我的其他 开源贡献 和 文章,请考虑通过成为 GitHub 上的赞助者或 Patreon 上的赞助人来激励我。
非常感谢任何捐赠,这真的很有帮助! 💯
请参阅文件 CONTRIBUTING.md。
此库在 MIT 许可证下发布。 有关详细信息,请参阅 LICENSE。