一个纯 Swift CSV 解析器和序列化器,带有用于符合 Codable
类型的相关编码器和解码器。
skelpo/CSV
包可以安装到任何具有 SPM 清单的项目中。将 .package
实例添加到 dependencies
数组
.package(url: "https://github.com/skelpo/CSV.git", from: "1.0.0")
并将 CSV
目标添加到任何您想要在其中使用该包的目标的依赖项中
.target(name: "App", dependencies: ["CSV"])
然后运行 swift package update
和 swift package generate-xcodeproj
(如果您正在使用 Xcode)。
您可以在这里找到生成的 API 文档。同时,这里概述了不同的解析和序列化方法是如何工作的
每种类型都有一个基本的异步版本,以及围绕它的同步包装器。Parser
是核心异步实现,而 SyncParser
是用于同步操作的包装器。
要创建一个 Parser
,您需要传入一个用于处理标头数据和单元格数据的处理程序。标头处理程序接收一个参数,即标头内容的字节数组 ([UInt8]
)。单元格处理程序接收两个参数,单元格的标头和单元格的内容。这些也都是字节数组。这两个处理程序都允许抛出错误。
注意: 为了能够调用 .parse(_:length:)
,您的解析器必须是一个变量。该方法会改变您无法访问的内部状态。我知道这很烦人。希望将来能修复这个问题。
您可以通过将数据块传递到 .parser(_:length:)
方法中来解析 CSV 数据,同时传递将要解析的 CSV 文件的总长度。这使我们能够正确解析最后一块数据,而不是永远不处理它。
当数据被解析时,解析器的处理程序将被调用并传递解析后的数据。解析方法返回一个 Result<Void, ErrorList>
类型。此方法已被标记为 @discardableResult
,因此您可以忽略返回的值。ErrorList
只是一个错误数组的包装器,它将是处理函数中抛出的错误(如果您从它们中抛出任何错误)。
这是一个 Parser
实例的示例
var data: [String: [String]] = [:]
var parser = Parser(
onHeader: { header in
let title = String(decoding: header, as: UTF8.self)
data[title] = []
},
onCell: { header, cell in
let title = String(decoding: header, as: UTF8.self)
let contents = String(decoding: cell, as: UTF8.self)
data[title, default: []].append(contents)
}
)
let length = chunks.reduce(0) { $0 + $1.count }
for chunk in chunks {
parser.parse(chunk, length: length)
}
如果您想同步解析整个 CSV 文档,可以使用 SyncParser
代替。此类型有两种方法,它们都接收一个字节数组并返回一个字典,该字典使用标头作为键,列是单元格数据的数组。其中一种方法变体将数据作为字节数组返回,另一种方法返回字符串。
let parser = SyncParser()
let data: [String: [String]] = parser.parse(csv)
列出解析器类型,这里有一个异步 Serializer
类型和一个对应的 SyncSerializer
类型。Serializer
初始化器接收一个行处理程序,当从传入的数据中序列化一行时,将调用该处理程序。这用于标头行和单元格行。
Serializer.serialize(_:)
方法接收 KeyedCollection
形式的数据,并带有各种泛型约束。您可以只传入 [String: [String]]
或 [[UInt8]: [[UInt8]]]
类型的字典。该协议主要用于包测试套件中的测试目的。
如果您序列化解析数据的块,您需要确保传入数据中的列都具有相同的长度,否则该方法将崩溃。如果您愿意,这是另一个进行 PR 的机会 😄。
这是一个 Serializer
可能的示例
let parsedData = ...
var rows: [[UInt8]] = []
var serializer = Serializer { row in
rows.append(row)
}
serializer.serialize(parsedData)
let document = rows.joined(separator: UInt8(ascii: "\n"))
SyncSerializer
接收的数据与 Serializer
相同,并将整个序列化的文档作为字节数组返回
let parsedData = ...
let serializer = SyncSerializer()
let document = serializer.serialize(parsedData)
CSVDecoder
处理以同步或异步方式将 CSV 数据解码为 Decodable
类型。您首先需要使用 CSVCodingOptions
创建一个 CSVDecoder
实例。这默认为 CSVCodingOptions
类型的 .default
实例。一旦您拥有 CSVDecoder
实例,您就可以根据需要获取 CSVSyncDecoder
或 CSVAsyncDecoder
。
要获取异步解码器,您可以使用 .async(for:length:_:)
方法。此方法接收 CSV 行将被解码成的类型,将要解码的 CSV 文档的总长度,以及在解码一行时被调用的处理程序。然后,您可以使用要解码的数据在 CSVAsyncDecoder
实例上调用 .decode(_:)
。此方法会抛出解码数据时发生的任何错误。
这是一个 CSVAsyncDecoder
的示例
let length = chunks.reduce(0) { $0 + $1.count }
let decoder = CSVDecoder().async(for: [String: String].self, length: length) { row in
database.insert(into: "table").values(row).run()
}
for chunk in chunks {
try decoder.decode(chunk)
}
还有 CSVSyncDecoder
,它的工作方式与您遇到的大多数其他解码器类似。您可以使用 CSVDecoder
类型上的 .sync
计算属性创建一个实例。同步解码器有一个 .decode(_:from:)
方法,它接收要将数据解码成的类型和要解码的 CSV 数据。然后,该方法返回一个传入类型的数组。
这是一个 CSVSyncDecoder
的示例
let decoder = CSVDecoder().sync
let data = try decoder.decode([String: String].self, from: data)
与 CSVDecoder
类似,您可以创建一个带有 CSVCodingOptions
实例的 CSVEncoder
,然后获取同步或异步版本来处理您的数据。
CSVEncoder.async(_:)
方法接收 1 个参数,即一个回调函数,该函数接收编码后的 CSV 行。然后,当您编码您的 Swift 类型实例时,它们将变成 CSV 行,您可以对它们执行您想要的操作。
这是一个 CSVAsyncEncoder
示例
var rows: [[UInt8]] = []
let encoder = CSVEncoder().async { row in
rows.append(row)
}
for data in encodables {
try encoder.encode(data)
}
let document = rows.joined(separator: UInt8(ascii: "\n"))
正如预期的那样,同步编码器接收一个 Encodable
类型的数组,并将其编码为 CSV 文档
let encoder = CSVEncoder().sync
let document = try encoder.encode(parsedData)
此包及其包含的任何内容均受MIT 许可协议约束。