You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+32-5Lines changed: 32 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -99,7 +99,7 @@ A `CSVReadder` parses CSV data from a given input (`String`, or `Data`, or file)
99
99
"""
100
100
let reader =tryCSVReader(input: string) { $0.headerStrategy= .firstLine }
101
101
102
-
let headers = reader.headers// ["numA", "numB", "numC"]
102
+
let headers = reader.headers// ["numA", "numB", "numC"]
103
103
let rowA =try reader.readRow() // ["1", "2", "3"]
104
104
let rowB =try reader.readRow() // ["4", "5", "6"]
105
105
```
@@ -316,7 +316,7 @@ let result = try decoder.decode(CustomType.self, from: data)
316
316
317
317
```swift
318
318
let decoder = CSVDecoder { $0.bufferingStrategy = .sequential }
319
-
let content: [Student] = try decoder([Student].self, from: URL("~/Desktop/Student.csv"))
319
+
let content: [Student] = try decoder.decode([Student].self, from: URL("~/Desktop/Student.csv"))
320
320
```
321
321
322
322
If you are dealing with a big CSV file, it is preferred to used direct file decoding, a `.sequential` or `.unrequested` buffering strategy, and set *presampling* to false; since then memory usage is drastically reduced.
@@ -325,17 +325,21 @@ If you are dealing with a big CSV file, it is preferred to used direct file deco
325
325
326
326
The decoding process can be tweaked by specifying configuration values at initialization time. `CSVDecoder` accepts the [same configuration values as `CSVReader`](#Reader-configuration) plus the following ones:
327
327
328
+
- `nilStrategy` (default: `.empty`) indicates how the `nil` *concept* (absence of value) is represented on the CSV.
329
+
330
+
- `boolStrategy` (default: `.insensitive`) defines how strings are decoded to `Bool` values.
331
+
328
332
- `floatStrategy` (default `.throw`) defines how to deal with non-conforming floating-point numbers (e.g. `NaN`).
329
333
330
334
- `decimalStrategy` (default `.locale`) indicates how strings are decoded to `Decimal` values.
331
335
332
-
- `dataStrategy` (default `.deferredToDate`) specify how strings are decoded to `Date` values.
336
+
- `dateStrategy` (default `.deferredToDate`) specify how strings are decoded to `Date` values.
333
337
334
338
- `dataStrategy` (default `.base64`) indicates how strings are decoded to `Data` values.
335
339
336
340
- `bufferingStrategy` (default `.keepAll`) controls the behavior of `KeyedDecodingContainer`s.
337
341
338
-
Selecting a buffering strategy directly affect the the decoding performance and the amount of memory used during the process. For more information check this README's [Tips using `Codable`](#Tips-using-codable) section and the [`Strategy.DecodingBuffer` definition](sources/Codable/Decodable/DecodingStrategy.swift).
342
+
Selecting a buffering strategy affects the the decoding performance and the amount of memory used during the process. For more information check this README's [Tips using `Codable`](#Tips-using-codable) section and the [`Strategy.DecodingBuffer` definition](sources/Codable/Decodable/DecodingStrategy.swift).
339
343
340
344
The configuration values can be set during `CSVDecoder` initialization or at any point before the `decode` function is called.
341
345
@@ -377,6 +381,10 @@ If you are dealing with a big CSV content, it is preferred to use direct file en
377
381
378
382
The encoding process can be tweaked by specifying configuration values. `CSVEncoder` accepts the [same configuration values as `CSVWriter`](#Writer-configuration) plus the following ones:
379
383
384
+
- `nilStrategy` (default: `.empty`) indicates how the `nil` *concept* (absence of value) is represented on the CSV.
385
+
386
+
- `boolStrategy` (default: `.deferredToString`) defines how Boolean values are encoded to `String` values.
387
+
380
388
- `floatStrategy` (default `.throw`) defines how to deal with non-conforming floating-point numbers (e.g. `NaN`).
381
389
382
390
- `decimalStrategy` (default `.locale`) indicates how decimal numbers are encoded to `String` values.
`Codable` is fairly easy to use and most Swift standard library types already conform to it. However, sometimes it is tricky to get custom types to comply to `Codable` for specific functionality. That is why I am leaving here some tips and advices concerning its usage:
The library has been heavily documented and any contribution is welcome. Please take a look at the [How to contribute](docs/CONTRIBUTING.md) document or peer into a more detailed roadmap on the [Github projects](https://github.com/dehesa/CodableCSV/projects).
610
+
611
+
### Community
612
+
613
+
If `CodableCSV` is not of your liking, the Swift community has other CSV solutions:
614
+
- [CSV.swift](https://github.com/yaslab/CSV.swift) is a simpler library with a focus on conforming to the [RFC4180](https://tools.ietf.org/html/rfc4180) standard.
615
+
616
+
It offers a great imperative CSV reader/writer and a row decoder. However, it lacks configurability (such as custom field/row delimiters, escaping scalar selection, presampling, etc.) and a CSV encoder. It doesn't support whole CSV file decoding and it doesn't mirror Foundation's JSON & PLIST decoder/encoder APIs.
617
+
618
+
- [SwiftCSV](https://github.com/swiftcsv/SwiftCSV) is an older/popular CSV parse-only library.
619
+
620
+
It offers a well-tested imperative CSV parser with a slower development cycle. It lacks an imperative writer, an encoder, a decoder, and parsing configuration values.
621
+
622
+
- [SwiftCSVExport](https://github.com/vigneshuvi/SwiftCSVExport) reads/writes CSV imperatively with great Objective-C support.
623
+
624
+
It offers an imperative CSV reader/writer relying on the Objective-C toolchain, which makes it great to use on Objective-C project.
625
+
626
+
There are many good tools outside the Swift community. Since writing them all would be a hard task, I will just point you to the great [AwesomeCSV](https://github.com/secretGeek/awesomeCSV) github repo. Take it a look! There are a lot of treasures to be found there.
/// The strategy to use for decoding `nil` representations.
50
+
publicenumNilDecoding{
51
+
/// An empty string is considered a `nil` value.
52
+
///
53
+
/// An empty string can be both the absence of characters between field delimiters and an empty escaped field (e.g. `""`).
54
+
case empty
55
+
/// Decodes the `nil` as a custom value decoded by the given closure.
56
+
/// - parameter decoding: Function receiving the CSV decoder used to parse a custom `nil` value.
57
+
/// - parameter decoder: The decoder on which to fetch a single value container to obtain the underlying `String` value.
58
+
/// - returns: Boolean indicating whether the encountered value was a `nil` representation. If the value is not supported, return `false`.
59
+
case custom(_ decoding:(_ decoder:Decoder)->Bool)
60
+
}
61
+
62
+
/// The strategy to use for decoding `Bool` values.
63
+
publicenumBoolDecoding{
64
+
/// Defer to `Bool`'s `LosslessStringConvertible` initializer.
65
+
///
66
+
/// For a value to be considered `true` or `false`, it must be a string with the exact value of `"true"` or `"false"`.
67
+
case deferredToBool
68
+
/// Decodes a Boolean from an underlying string value by transforming `true`/`false` and `yes`/`no` disregarding case sensitivity.
69
+
///
70
+
/// The value: `True`, `TRUE`, `TruE` or `YES`are accepted.
71
+
case insensitive
72
+
/// Decodes the `Bool` as a custom value decoded by the given closure.
73
+
///
74
+
/// If the closure fails to decode a value from the given decoder, the error will be bubled up.
75
+
/// - parameter decoding: Function receiving the CSV decoder used to parse a custom `Bool` value.
76
+
/// - parameter decoder: The decoder on which to fetch a single value container to obtain the underlying `String` value.
77
+
/// - returns: Boolean value decoded from the underlying storage.
78
+
case custom(_ decoding:(_ decoder:Decoder)throws->Bool)
79
+
}
80
+
43
81
/// The strategy to use for decoding `Decimal` values.
44
82
publicenumDecimalDecoding{
45
83
/// The locale used to interpret the number (specifically `decimalSeparator`).
46
84
case locale(Locale?=nil)
47
85
/// Decode the `Decimal` as a custom value decoded by the given closure.
48
-
case custom((_ decoder:Decoder)throws->Decimal)
86
+
///
87
+
/// If the closure fails to decode a value from the given decoder, the error will be bubled up.
88
+
/// - parameter decoding: Function receiving the CSV decoder used to parse a custom `Decimal` value.
89
+
/// - parameter decoder: The decoder on which to fetch a single value container to obtain the underlying `String` value.
90
+
/// - returns: `Decimal` value decoded from the underlying storage.
91
+
case custom(_ decoding:(_ decoder:Decoder)throws->Decimal)
49
92
}
50
93
51
94
/// The strategy to use for decoding `Date` values.
@@ -61,7 +104,12 @@ extension Strategy {
61
104
/// Decode the `Date` as a string parsed by the given formatter.
62
105
case formatted(DateFormatter)
63
106
/// Decode the `Date` as a custom value decoded by the given closure.
64
-
case custom((_ decoder:Decoder)throws->Date)
107
+
///
108
+
/// If the closure fails to decode a value from the given decoder, the error will be bubled up.
109
+
/// - parameter decoding: Function receiving the CSV decoder used to parse a custom `Date` value.
110
+
/// - parameter decoder: The decoder on which to fetch a single value container to obtain the underlying `String` value.
111
+
/// - returns: `Date` value decoded from the underlying storage.
112
+
case custom(_ decoding:(_ decoder:Decoder)throws->Date)
65
113
}
66
114
67
115
/// The strategy to use for decoding `Data` values.
@@ -71,7 +119,12 @@ extension Strategy {
71
119
/// Decode the `Data` from a Base64-encoded string.
72
120
case base64
73
121
/// Decode the `Data` as a custom value decoded by the given closure.
74
-
case custom((_ decoder:Decoder)throws->Data)
122
+
///
123
+
/// If the closure fails to decode a value from the given decoder, the error will be bubled up.
124
+
/// - parameter decoding: Function receiving the CSV decoder used to parse a custom `Data` value.
125
+
/// - parameter decoder: The decoder on which to fetch a single value container to obtain the underlying `String` value.
126
+
/// - returns: `Data` value decoded from the underlying storage.
127
+
case custom(_ decoding:(_ decoder:Decoder)throws->Data)
75
128
}
76
129
77
130
/// Indication of how many rows are cached for reuse by the decoder.
@@ -86,15 +139,15 @@ extension Strategy {
86
139
/// Forward/Backwards decoding jumps are allowed. A row that has been previously decoded can be decoded again.
87
140
/// - remark: This strategy consumes the largest amount of memory from all the supported options.
88
141
case keepAll
142
+
// /// Only CSV fields that have been decoded but not requested by the user are being kept in memory.
143
+
// ///
144
+
// /// *Keyed containers* can be used to read rows/fields unordered. However, previously requested rows cannot be requested again or an error will be thrown.
145
+
// /// - remark: This strategy tries to keep the cache to a minimum, but memory usage may be big if the user doesn't request intermediate rows.
146
+
// case unrequested
89
147
/// No rows are kept in memory (except for the CSV row being decoded at the moment)
90
148
///
91
149
/// *Keyed containers* can be used, but at a file-level any forward jump will discard the in-between rows. At a row-level *keyed containers* may still be used for random-order reading.
92
150
/// - remark: This strategy provides the smallest usage of memory from them all.
93
151
case sequential
94
-
/// Only CSV fields that have been decoded but not requested by the user are being kept in memory.
95
-
///
96
-
/// *Keyed containers* can be used to read rows/fields unordered. However, previously requested rows cannot be requested again or an error will be thrown.
97
-
/// - remark: This strategy tries to keep the cache to a minimum, but memory usage may be big if the user doesn't request intermediate rows.
0 commit comments