CSV

一个纯 Swift CSV 解析器和序列化器,带有用于符合 Codable 类型的相关编码器和解码器。

📦 Swift 包管理器

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 updateswift package generate-xcodeproj(如果您正在使用 Xcode)。

🛠 API

您可以在这里找到生成的 API 文档。同时,这里概述了不同的解析和序列化方法是如何工作的

解析器 (Parser)

每种类型都有一个基本的异步版本,以及围绕它的同步包装器。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)

列出解析器类型,这里有一个异步 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)

解码器 (Decoder)

CSVDecoder 处理以同步或异步方式将 CSV 数据解码为 Decodable 类型。您首先需要使用 CSVCodingOptions 创建一个 CSVDecoder 实例。这默认为 CSVCodingOptions 类型的 .default 实例。一旦您拥有 CSVDecoder 实例,您就可以根据需要获取 CSVSyncDecoderCSVAsyncDecoder

要获取异步解码器,您可以使用 .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)

编码器 (Encoder)

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)

📄 许可证 (License)

此包及其包含的任何内容均受MIT 许可协议约束。