-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCHCSVParser.h
More file actions
529 lines (448 loc) · 19.8 KB
/
CHCSVParser.h
File metadata and controls
529 lines (448 loc) · 19.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
//
// CHCSVParser.h
// CHCSVParser
/**
Copyright (c) 2014 Dave DeLong
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
**/
#import <Foundation/Foundation.h>
#ifndef NS_DESIGNATED_INITIALIZER
#define NS_DESIGNATED_INITIALIZER
#endif
#ifndef CHCSV_DEPRECATED
#define CHCSV_DEPRECATED(...) __attribute__((deprecated("" #__VA_ARGS__)))
#endif
extern NSString * const CHCSVErrorDomain;
typedef NS_ENUM(NSInteger, CHCSVErrorCode) {
/**
* Indicates that a delimited file is incorrectly formatted.
* For example, perhaps a double quote is in the wrong position.
*/
CHCSVErrorCodeInvalidFormat = 1,
/**
* When using @c CHCSVParserOptionsUsesFirstLineAsKeys, all of the lines in the file
* must have the same number of fields. If they do not, parsing is aborted and this error is returned.
*/
CHCSVErrorCodeIncorrectNumberOfFields,
};
@class CHCSVParser;
@protocol CHCSVParserDelegate <NSObject>
@optional
/**
* Indicates that the parser has started parsing the stream
*
* @param parser The @c CHCSVParser instance
*/
- (void)parserDidBeginDocument:(CHCSVParser *)parser;
/**
* Indicates that the parser has successfully finished parsing the stream
*
* This method is not invoked if any error is encountered
*
* @param parser The @c CHCSVParser instance
*/
- (void)parserDidEndDocument:(CHCSVParser *)parser;
/**
* Indicates the parser has started parsing a line
*
* @param parser The @c CHCSVParser instance
* @param recordNumber The 1-based number of the record
*/
- (void)parser:(CHCSVParser *)parser didBeginLine:(NSUInteger)recordNumber;
/**
* Indicates the parser has finished parsing a line
*
* @param parser The @c CHCSVParser instance
* @param recordNumber The 1-based number of the record
*/
- (void)parser:(CHCSVParser *)parser didEndLine:(NSUInteger)recordNumber;
/**
* Indicates the parser has parsed a field on the current line
*
* @param parser The @c CHCSVParser instance
* @param field The parsed string. If configured to do so, this string may be sanitized and trimmed
* @param fieldIndex The 0-based index of the field within the current record
*/
- (void)parser:(CHCSVParser *)parser didReadField:(NSString *)field atIndex:(NSInteger)fieldIndex;
/**
* Indicates the parser has encountered a comment
*
* This method is only invoked if @c CHCSVParser.recognizesComments is @c YES
*
* @param parser The @c CHCSVParser instance
* @param comment The parsed comment
*/
- (void)parser:(CHCSVParser *)parser didReadComment:(NSString *)comment;
/**
* Indicates the parser encounter an error while parsing
*
* @param parser The @c CHCSVParser instance
* @param error The @c NSError instance
*/
- (void)parser:(CHCSVParser *)parser didFailWithError:(NSError *)error;
@end
@interface CHCSVParser : NSObject
/**
* The delegate for the @c CHCSVParser
*/
@property (assign) id<CHCSVParserDelegate> delegate;
/**
* If @c YES, then the parser will removing surrounding double quotes and will unescape characters.
* The default value is @c NO.
* @warning Do not mutate this property after parsing has begun
*/
@property (nonatomic, assign) BOOL sanitizesFields;
/**
* If @c YES, then the parser will trim whitespace around fields. If @c sanitizesFields is also @c YES,
* then the sanitized field is also trimmed. The default value is @c NO.
* @warning Do not mutate this property after parsing has begun
*/
@property (nonatomic, assign) BOOL trimsWhitespace;
/**
* If @c YES, then the parser will allow special characters (delimiter, newline, quote, etc)
* to be escaped within a field using a backslash character. The default value is @c NO.
* @warning Do not mutate this property after parsing has begun
*/
@property (nonatomic, assign) BOOL recognizesBackslashesAsEscapes;
/**
* If @c YES, then the parser will interpret any field that begins with an octothorpe as a comment.
* Comments are terminated using an unescaped newline character. The default value is @c NO.
* @warning Do not mutate this property after parsing has begun
*/
@property (nonatomic, assign) BOOL recognizesComments;
/**
* If @c YES, then quoted fields may begin with an equal sign.
* Some programs produce fields with a leading equal sign to indicate that the contents must be represented exactly.
* The default value is @c NO.
* @warning Do not mutate this property after parsing has begun
*/
@property (nonatomic, assign) BOOL recognizesLeadingEqualSign;
/**
* The number of bytes that have been read from the input stream so far
*
* This property is key-value observable.
*/
@property (readonly) NSUInteger totalBytesRead;
/**
* This method is unavailable, because there is nothing supplied to parse.
*/
- (instancetype)init NS_UNAVAILABLE;
/**
* The designated initializer
*
* @param stream The @c NSInputStream from which bytes will be read and parsed. Must not be @c nil
* @param encoding A pointer to an @c NSStringEncoding. If non-nil, this will be filled in with the encoding used to parse the stream
* @param delimiter The delimiter character to be used when parsing the stream. Must not be @c nil, and may not be the double quote character
*
* @return a @c CHCSVParser instance, or @c nil if initialization failed
*/
- (instancetype)initWithInputStream:(NSInputStream *)stream usedEncoding:(inout NSStringEncoding *)encoding delimiter:(unichar)delimiter NS_DESIGNATED_INITIALIZER;
/**
* An initializer to parse a CSV string
*
* Internally it calls the designated initializer and provides a stream of the UTF8 representation of the string as well as the comma delimiter.
*
* @param csv The @c NSString to parse. Must not be @c nil
*
* @return a @c CHCSVParser instance, or @c nil if initialization failed
*/
- (instancetype)initWithCSVString:(NSString *)csv;
/**
* An initializer to parse a delimited string
*
* Internally it calls the designated initializer and provides a stream of the UTF8 representation of the string as well as the provided delimiter.
*
* @param string The @c NSString to parse. Must not be @c nil
* @param delimiter The delimiter character to be used when parsing the string. Must not be @c nil, and may not be the double quote character
*
* @return a @c CHCSVParser instance, or @c nil if initialization failed
*/
- (instancetype)initWithDelimitedString:(NSString *)string delimiter:(unichar)delimiter;
/**
* An initializer to parse the contents of URL
*
* Internally it calls the designated initializer and provides a stream to the URL as well as the comma delimiter.
* The parser attempts to infer the encoding from the stream.
*
* @param csvURL The @c NSURL to the CSV file
*
* @return a @c CHCSVParser instance, or @c nil if initialization failed
*/
- (instancetype)initWithContentsOfCSVURL:(NSURL *)csvURL;
/**
* An initializer to parse the contents of URL
*
* Internally it calls the designated initializer and provides a stream to the URL as well as the provided delimiter.
* The parser attempts to infer the encoding from the stream.
*
* @param URL The @c NSURL to the delimited file
* @param delimiter The delimiter character to be used when parsing the string. Must not be @c nil, and may not be the double quote character
*
* @return a @c CHCSVParser instance, or @c nil if initialization failed
*/
- (instancetype)initWithContentsOfDelimitedURL:(NSURL *)URL delimiter:(unichar)delimiter;
/**
* Instruct the parser to begin parsing
*
* You should not invoke this method more than once.
*/
- (void)parse;
/**
* Instruct the parser to abort parsing
*
* Invoking this method after parsing has completed has no effect.
*/
- (void)cancelParsing;
@end
@interface CHCSVWriter : NSObject
/**
* This method is unavailable, because there is no way to extract the written CSV.
*/
- (instancetype)init NS_UNAVAILABLE;
/**
* Initializes a @c CHCSVWriter to write to the provided file path. Assumes @c NSUTF8Encoding and the comma delimiter
*
* @param path The path to the CSV file
*
* @return a @c CHCSVWriter instance, or @c nil if initialization failed
*/
- (instancetype)initForWritingToCSVFile:(NSString *)path;
/**
* The designated initializer
*
* @param stream The @c NSOutputStream to which bytes will be written.
* If you wish to append to an existing file, you can provide an @c NSOutputStream that is set to append to the target file
* @param encoding The byte encoding to use when writing strings to the stream
* @param delimiter The field delimiter to use during writing
*
* @return a @c CHCSVWriter instance, or @c nil if initialization failed
*/
- (instancetype)initWithOutputStream:(NSOutputStream *)stream encoding:(NSStringEncoding)encoding delimiter:(unichar)delimiter NS_DESIGNATED_INITIALIZER;
/**
* Write a field to the output stream
*
* If necessary, this will also write a delimiter to the stream as well. This method takes care of all escaping.
*
* @param field The object to be written to the stream
* If you provide an object that is not an @c NSString, its @c description will be written to the stream.
*/
- (void)writeField:(id)field;
/**
* Write a newline character to the output stream
*/
- (void)finishLine;
/**
* Write a series of fields to the stream as a new line
*
* If another line is already in progress, it is terminated and a new line is begun.
* This method iteratively invokes @c writeField:, followed by @c finishLine.
*
* @param fields A sequence of fields to be written
*/
- (void)writeLineOfFields:(id<NSFastEnumeration>)fields;
/**
* Write the contents of an @c NSDictionary as a new line
*
* @param dictionary The @c NSDictionary whose values will be written to the output stream.
* Values will be written in the order specified by the first line of fields written to the stream.
* If no lines have been written yet, this method will throw an exception.
*/
- (void)writeLineWithDictionary:(NSDictionary *)dictionary;
/**
* Write a comment to the stream
*
* If another line is already in progress, it is terminated and a new line is begun.
* The new line will be started with the octothorpe (#) character, followed by the comment.
* The comment is terminated using @c finishLine
*
* @param comment The comment to be written to the stream
*/
- (void)writeComment:(NSString *)comment;
/**
* Closes the output stream.
* You do not have to invoke this method yourself, as it is invoked during deallocation.
*/
- (void)closeStream;
@end
#pragma mark - Convenience Categories
typedef NS_OPTIONS(NSUInteger, CHCSVParserOptions) {
/**
* Allow backslash to escape special characters.
* If you specify this option, you may not use a backslash as the delimiter.
* @see CHCSVParser.recognizesBackslashesAsEscapes
*/
CHCSVParserOptionsRecognizesBackslashesAsEscapes = 1 << 0,
/**
* Cleans the field before reporting it.
* @see CHCSVParser.sanitizesFields
*/
CHCSVParserOptionsSanitizesFields = 1 << 1,
/**
* Fields that begin with a "#" will be reported as comments.
* If you specify this option, you may not use an octothorpe as the delimiter.
* @see CHCSVParser.recognizesComments
*/
CHCSVParserOptionsRecognizesComments = 1 << 2,
/**
* Trims whitespace around a field.
* @see CHCSVParser.trimsWhitespace
*/
CHCSVParserOptionsTrimsWhitespace = 1 << 3,
/**
* When you specify this option, instead of getting an Array of Arrays of Strings,
* you get an Array of @c CHCSVOrderedDictionary instances.
* If the file only contains a single line, then an empty array is returned.
*/
CHCSVParserOptionsUsesFirstLineAsKeys = 1 << 4,
/**
* Some delimited files contain fields that begin with a leading equal sign,
* to indicate that the contents should not be summarized or re-interpreted.
* (For example, to remove insignificant digits)
* If you specify this option, you may not use an equal sign as the delimiter.
* @see CHCSVParser.recognizesLeadingEqualSign
* @link http://edoceo.com/utilitas/csv-file-format
*/
CHCSVParserOptionsRecognizesLeadingEqualSign = 1 << 5
};
/**
* An @c NSDictionary subclass that maintains a strong ordering of its key-value pairs
*/
@interface CHCSVOrderedDictionary : NSDictionary
- (instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (id)objectAtIndex:(NSUInteger)idx;
@end
@interface NSArray (CHCSVAdditions)
/**
* A convenience constructor to parse a CSV file
*
* @param fileURL The @c NSURL to the CSV file
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise
*/
+ (instancetype)arrayWithContentsOfCSVURL:(NSURL *)fileURL;
/**
* A convenience constructor to parse a CSV file
*
* @param fileURL The @c NSURL to the CSV file
* @param options A bitwise-OR of @c CHCSVParserOptions to control how parsing should occur
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise
*/
+ (instancetype)arrayWithContentsOfCSVURL:(NSURL *)fileURL options:(CHCSVParserOptions)options;
/**
* A convenience constructor to parse a delimited file
*
* @param fileURL The @c NSURL to the delimited file
* @param delimiter The delimiter used in the file
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise
*/
+ (instancetype)arrayWithContentsOfDelimitedURL:(NSURL *)fileURL delimiter:(unichar)delimiter;
/**
* A convenience constructor to parse a delimited file
*
* @param fileURL The @c NSURL to the delimited file
* @param options A bitwise-OR of @c CHCSVParserOptions to control how parsing should occur
* @param delimiter The delimiter used in the file
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise
*/
+ (instancetype)arrayWithContentsOfDelimitedURL:(NSURL *)fileURL options:(CHCSVParserOptions)options delimiter:(unichar)delimiter;
/**
* A convenience constructor to parse a delimited file
*
* @param fileURL The @c NSURL to the delimited file
* @param options A bitwise-OR of @c CHCSVParserOptions to control how parsing should occur
* @param delimiter The delimiter used in the file
* @param error A pointer to an @c NSError*, which will be filled in if parsing fails
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise.
*/
+ (instancetype)arrayWithContentsOfDelimitedURL:(NSURL *)fileURL options:(CHCSVParserOptions)options delimiter:(unichar)delimiter error:(NSError *__autoreleasing *)error;
/**
* If the receiver is an @c NSArray of @c NSArrays of objects, this will turn it into a comma-delimited string
* Returns the string of CSV, if writing succeeds; @c nil otherwise.
*/
- (NSString *)CSVString;
@end
@interface NSString (CHCSVAdditions)
/**
* Parses the receiver as a comma-delimited string
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise.
*/
@property (nonatomic, readonly) NSArray *CSVComponents;
/**
* Parses the receiver as a comma-delimited string
*
* @param options A bitwise-OR of @c CHCSVParserOptions to control how parsing should occur
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise.
*/
- (NSArray *)CSVComponentsWithOptions:(CHCSVParserOptions)options;
/**
* Parses the receiver as a delimited string
*
* @param delimiter The delimiter used in the string
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise.
*/
- (NSArray *)componentsSeparatedByDelimiter:(unichar)delimiter;
/**
* Parses the receiver as a delimited string
*
* @param delimiter The delimiter used in the string
* @param options A bitwise-OR of @c CHCSVParserOptions to control how parsing should occur
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise.
*/
- (NSArray *)componentsSeparatedByDelimiter:(unichar)delimiter options:(CHCSVParserOptions)options;
/**
* Parses the receiver as a delimited string
*
* @param delimiter The delimiter used in the string
* @param options A bitwise-OR of @c CHCSVParserOptions to control how parsing should occur
* @param error A pointer to an @c NSError*, which will be filled in if parsing fails
*
* @return An @c NSArray of @c NSArrays of @c NSStrings, if parsing succeeds; @c nil otherwise.
*/
- (NSArray *)componentsSeparatedByDelimiter:(unichar)delimiter options:(CHCSVParserOptions)options error:(NSError *__autoreleasing *)error;
@end
#pragma mark - Deprecated stuff
/**
* These methods have been deprecated, but are preserved here for source compatibility.
* They will be removed in the future.
*/
@interface CHCSVParser (Deprecated)
@property (nonatomic, assign) BOOL stripsLeadingAndTrailingWhitespace CHCSV_DEPRECATED(@"use .trimsWhitespace instead"); // default is NO
- (instancetype)initWithCSVString:(NSString *)csv delimiter:(unichar)delimiter CHCSV_DEPRECATED("use -initWithDelimitedString:delimiter: instead");
- (instancetype)initWithContentsOfCSVFile:(NSString *)csvFilePath CHCSV_DEPRECATED("use -initWithContentsOfCSVURL: instead");
- (instancetype)initWithContentsOfCSVFile:(NSString *)csvFilePath delimiter:(unichar)delimiter CHCSV_DEPRECATED("use -initWithContentsOfDelimitedURL:delimiter: instead");
@end
@interface NSArray (CHCSVAdditions_Deprecated)
+ (instancetype)arrayWithContentsOfCSVFile:(NSString *)csvFilePath CHCSV_DEPRECATED("Use +arrayWithContentsOfCSVURL: instead");
+ (instancetype)arrayWithContentsOfCSVFile:(NSString *)csvFilePath delimiter:(unichar)delimiter CHCSV_DEPRECATED("Use +arrayWithContentsOfDelimitedURL:delimiter: instead");
+ (instancetype)arrayWithContentsOfCSVFile:(NSString *)csvFilePath options:(CHCSVParserOptions)options CHCSV_DEPRECATED("Use +arrayWithContentsOfCSVURL:options: instead");
+ (instancetype)arrayWithContentsOfCSVFile:(NSString *)csvFilePath options:(CHCSVParserOptions)options delimiter:(unichar)delimiter CHCSV_DEPRECATED("Use +arrayWithContentsOfDelimitedURL:options:delimiter: instead");
+ (instancetype)arrayWithContentsOfCSVFile:(NSString *)csvFilePath options:(CHCSVParserOptions)options delimiter:(unichar)delimiter error:(NSError *__autoreleasing *)error CHCSV_DEPRECATED("Use +arrayWithContentsOfDelimitedURL:options:delimiter:error: instead");
@end
@interface NSString (CHCSVAdditions_Deprecated)
- (NSArray *)CSVComponentsWithDelimiter:(unichar)delimiter CHCSV_DEPRECATED("Use -componentsSeparatedByDelimiter: instead");
- (NSArray *)CSVComponentsWithOptions:(CHCSVParserOptions)options delimiter:(unichar)delimiter CHCSV_DEPRECATED("Use -componentsSeparatedByDelimiter:options: instead");
- (NSArray *)CSVComponentsWithOptions:(CHCSVParserOptions)options delimiter:(unichar)delimiter error:(NSError *__autoreleasing *)error CHCSV_DEPRECATED("Use -componentsSeparatedByDelimiter:options:error: instead");
@end