Skip to content
This repository
Browse code

Added the ability to parse a string. Added relevant unit tests and fi…

…xed atomic writing bug
  • Loading branch information...
commit 7cba09d03f8e9a9553ec1aaae6442a0773122d69 1 parent 6866bdf
Dave DeLong authored October 02, 2010
12  CHCSV.h
... ...
@@ -0,0 +1,12 @@
  1
+//
  2
+//  CHCSV.h
  3
+//  CHCSVParser
  4
+//
  5
+//  Created by Dave DeLong on 10/2/10.
  6
+//  Copyright 2010 Home. All rights reserved.
  7
+//
  8
+
  9
+#import "CHCSVParser.h"
  10
+#import "CHCSVWriter.h"
  11
+#import "NSArray+CHCSVAdditions.h"
  12
+#import "NSString+CHCSVAdditions.h"
3  CHCSVParser.h
@@ -53,6 +53,9 @@
53 53
 
54 54
 - (id) initWithContentsOfCSVFile:(NSString *)aCSVFile encoding:(NSStringEncoding)encoding error:(NSError **)anError;
55 55
 - (id) initWithContentsOfCSVFile:(NSString *)aCSVFile usedEncoding:(NSStringEncoding *)usedEncoding error:(NSError **)anError;
  56
+
  57
+- (id) initWithCSVString:(NSString *)csvString encoding:(NSStringEncoding)encoding error:(NSError **)anError;
  58
+
56 59
 - (void) parse;
57 60
 
58 61
 @end
63  CHCSVParser.m
@@ -66,7 +66,7 @@ @interface CHCSVParser ()
66 66
 
67 67
 @property (retain) NSString * currentChunk;
68 68
 
69  
-- (void) discoverTextEncoding;
  69
+- (NSStringEncoding) textEncodingForData:(NSData *)chunkToSniff offset:(NSUInteger *)offset;
70 70
 
71 71
 - (NSString *) nextCharacter;
72 72
 - (void) runParseLoop;
@@ -90,6 +90,7 @@ - (id) initWithContentsOfCSVFile:(NSString *)aCSVFile encoding:(NSStringEncoding
90 90
 		csvFileHandle = [[NSFileHandle fileHandleForReadingAtPath:csvFile] retain];
91 91
 		if (csvFileHandle == nil) {
92 92
 			if (anError) {
  93
+				NSLog(@"error for file: %@", csvFile);
93 94
 				*anError = [NSError errorWithDomain:@"com.davedelong.csv" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unable to open file for reading" forKey:NSLocalizedDescriptionKey]];
94 95
 			}
95 96
 			[self release];
@@ -113,7 +114,11 @@ - (id) initWithContentsOfCSVFile:(NSString *)aCSVFile encoding:(NSStringEncoding
113 114
 
114 115
 - (id) initWithContentsOfCSVFile:(NSString *)aCSVFile usedEncoding:(NSStringEncoding *)usedEncoding error:(NSError **)anError {
115 116
 	if (self = [self initWithContentsOfCSVFile:aCSVFile encoding:NSUTF8StringEncoding error:anError]) {
116  
-		[self discoverTextEncoding];		
  117
+		
  118
+		NSData * chunk = [csvFileHandle readDataOfLength:CHUNK_SIZE];
  119
+		NSUInteger seekOffset = 0;
  120
+		fileEncoding = [self textEncodingForData:chunk offset:&seekOffset];
  121
+		[csvFileHandle seekToFileOffset:seekOffset];
117 122
 		
118 123
 		if (usedEncoding) {
119 124
 			*usedEncoding = fileEncoding;
@@ -122,6 +127,26 @@ - (id) initWithContentsOfCSVFile:(NSString *)aCSVFile usedEncoding:(NSStringEnco
122 127
 	return self;
123 128
 }
124 129
 
  130
+- (id) initWithCSVString:(NSString *)csvString encoding:(NSStringEncoding)encoding error:(NSError **)anError {
  131
+	if (self = [super init]) {
  132
+		csvFile = nil;
  133
+		csvFileHandle = nil;
  134
+		fileEncoding = encoding;
  135
+		
  136
+		balancedQuotes = YES;
  137
+		balancedEscapes = YES;
  138
+		
  139
+		currentLine = 0;
  140
+		currentField = [[NSMutableString alloc] init];
  141
+		
  142
+		currentChunk = [csvString copy];
  143
+		chunkIndex = 0;
  144
+		
  145
+		state = CHCSVParserStateInsideFile;
  146
+	}
  147
+	return self;
  148
+}
  149
+
125 150
 - (void) dealloc {
126 151
 	[csvFileHandle release];
127 152
 	[csvFile release];
@@ -132,51 +157,51 @@ - (void) dealloc {
132 157
 	[super dealloc];
133 158
 }
134 159
 
135  
-- (void) discoverTextEncoding {
136  
-	NSData * chunkToSniff = [csvFileHandle readDataOfLength:CHUNK_SIZE];
  160
+- (NSStringEncoding) textEncodingForData:(NSData *)chunkToSniff offset:(NSUInteger *)offset {
137 161
 	NSUInteger length = [chunkToSniff length];
138  
-	NSUInteger offset = 0;
  162
+	*offset = 0;
  163
+	NSStringEncoding encoding = NSUTF8StringEncoding;
139 164
 	
140 165
 	if (length > 0) {
141 166
 		UInt8* bytes = (UInt8*)[chunkToSniff bytes];
142  
-		fileEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding());
  167
+		encoding = CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding());
143 168
 		switch (bytes[0]) {
144 169
 			case 0x00:
145 170
 				if (length>3 && bytes[1]==0x00 && bytes[2]==0xFE && bytes[3]==0xFF) {
146  
-					fileEncoding = NSUTF32BigEndianStringEncoding;
147  
-					offset = 4;
  171
+					encoding = NSUTF32BigEndianStringEncoding;
  172
+					*offset = 4;
148 173
 				}
149 174
 				break;
150 175
 			case 0xEF:
151 176
 				if (length>2 && bytes[1]==0xBB && bytes[2]==0xBF) {
152  
-					fileEncoding = NSUTF8StringEncoding;
153  
-					offset = 3;
  177
+					encoding = NSUTF8StringEncoding;
  178
+					*offset = 3;
154 179
 				}
155 180
 				break;
156 181
 			case 0xFE:
157 182
 				if (length>1 && bytes[1]==0xFF) {
158  
-					fileEncoding = NSUTF16BigEndianStringEncoding;
159  
-					offset = 2;
  183
+					encoding = NSUTF16BigEndianStringEncoding;
  184
+					*offset = 2;
160 185
 				}
161 186
 				break;
162 187
 			case 0xFF:
163 188
 				if (length>1 && bytes[1]==0xFE) {
164 189
 					if (length>3 && bytes[2]==0x00 && bytes[3]==0x00) {
165  
-						fileEncoding = NSUTF32LittleEndianStringEncoding;
166  
-						offset = 4;
  190
+						encoding = NSUTF32LittleEndianStringEncoding;
  191
+						*offset = 4;
167 192
 					} else {
168  
-						fileEncoding = NSUTF16LittleEndianStringEncoding;
169  
-						offset = 2;
  193
+						encoding = NSUTF16LittleEndianStringEncoding;
  194
+						*offset = 2;
170 195
 					}
171 196
 				}
172 197
 				break;
173 198
 			default:
174  
-				fileEncoding = NSUTF8StringEncoding; // fall back on UTF8
  199
+				encoding = NSUTF8StringEncoding; // fall back on UTF8
175 200
 				break;
176 201
 		}
177 202
 	}
178  
-	[csvFileHandle seekToFileOffset:offset];
179  
-	return;
  203
+	
  204
+	return encoding;
180 205
 }
181 206
 
182 207
 #pragma mark Parsing methods
45  CHCSVParser.xcodeproj/project.pbxproj
@@ -7,6 +7,10 @@
7 7
 	objects = {
8 8
 
9 9
 /* Begin PBXBuildFile section */
  10
+		5516BCB512578CFC0025F235 /* NSString+CHCSVAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5516BCB412578CFC0025F235 /* NSString+CHCSVAdditions.m */; };
  11
+		5516BCB912578D750025F235 /* CHCSVSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5516BCB812578D750025F235 /* CHCSVSupport.m */; };
  12
+		5516BCBB12578EA90025F235 /* NSString+CHCSVAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5516BCB412578CFC0025F235 /* NSString+CHCSVAdditions.m */; };
  13
+		5516BCBC12578EAD0025F235 /* CHCSVSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5516BCB812578D750025F235 /* CHCSVSupport.m */; };
10 14
 		551981D61203715400FBE033 /* CHCSVParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 551981D51203715400FBE033 /* CHCSVParser.m */; };
11 15
 		557FCEB61203F938009FCDBA /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 557FCEB51203F938009FCDBA /* CoreServices.framework */; };
12 16
 		557FD0431204A45D009FCDBA /* NSArray+CHCSVAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 557FD0421204A45D009FCDBA /* NSArray+CHCSVAdditions.m */; };
@@ -37,6 +41,11 @@
37 41
 		08FB7796FE84155DC02AAC07 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
38 42
 		08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
39 43
 		32A70AAB03705E1F00C91783 /* CHCSVParser_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHCSVParser_Prefix.pch; sourceTree = "<group>"; };
  44
+		5516BCB312578CFC0025F235 /* NSString+CHCSVAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+CHCSVAdditions.h"; sourceTree = "<group>"; };
  45
+		5516BCB412578CFC0025F235 /* NSString+CHCSVAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+CHCSVAdditions.m"; sourceTree = "<group>"; };
  46
+		5516BCB612578D480025F235 /* CHCSV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHCSV.h; sourceTree = "<group>"; };
  47
+		5516BCB712578D750025F235 /* CHCSVSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHCSVSupport.h; sourceTree = "<group>"; };
  48
+		5516BCB812578D750025F235 /* CHCSVSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHCSVSupport.m; sourceTree = "<group>"; };
40 49
 		551981D41203715400FBE033 /* CHCSVParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHCSVParser.h; sourceTree = "<group>"; };
41 50
 		551981D51203715400FBE033 /* CHCSVParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHCSVParser.m; sourceTree = "<group>"; };
42 51
 		551981EE1203800300FBE033 /* Test.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Test.csv; sourceTree = "<group>"; };
@@ -91,12 +100,7 @@
91 100
 			children = (
92 101
 				32A70AAB03705E1F00C91783 /* CHCSVParser_Prefix.pch */,
93 102
 				08FB7796FE84155DC02AAC07 /* main.m */,
94  
-				551981D41203715400FBE033 /* CHCSVParser.h */,
95  
-				551981D51203715400FBE033 /* CHCSVParser.m */,
96  
-				55EFC7B71210608C0070B303 /* CHCSVWriter.h */,
97  
-				55EFC7B81210608C0070B303 /* CHCSVWriter.m */,
98  
-				557FD0411204A45D009FCDBA /* NSArray+CHCSVAdditions.h */,
99  
-				557FD0421204A45D009FCDBA /* NSArray+CHCSVAdditions.m */,
  103
+				5516BCBA12578DA90025F235 /* CHCSVParser */,
100 104
 				551981EE1203800300FBE033 /* Test.csv */,
101 105
 				557FD05A1204A72B009FCDBA /* UnitTests.h */,
102 106
 				557FD05B1204A72B009FCDBA /* UnitTests.m */,
@@ -121,6 +125,24 @@
121 125
 			name = Products;
122 126
 			sourceTree = "<group>";
123 127
 		};
  128
+		5516BCBA12578DA90025F235 /* CHCSVParser */ = {
  129
+			isa = PBXGroup;
  130
+			children = (
  131
+				5516BCB612578D480025F235 /* CHCSV.h */,
  132
+				551981D41203715400FBE033 /* CHCSVParser.h */,
  133
+				551981D51203715400FBE033 /* CHCSVParser.m */,
  134
+				55EFC7B71210608C0070B303 /* CHCSVWriter.h */,
  135
+				55EFC7B81210608C0070B303 /* CHCSVWriter.m */,
  136
+				5516BCB712578D750025F235 /* CHCSVSupport.h */,
  137
+				5516BCB812578D750025F235 /* CHCSVSupport.m */,
  138
+				557FD0411204A45D009FCDBA /* NSArray+CHCSVAdditions.h */,
  139
+				557FD0421204A45D009FCDBA /* NSArray+CHCSVAdditions.m */,
  140
+				5516BCB312578CFC0025F235 /* NSString+CHCSVAdditions.h */,
  141
+				5516BCB412578CFC0025F235 /* NSString+CHCSVAdditions.m */,
  142
+			);
  143
+			name = CHCSVParser;
  144
+			sourceTree = "<group>";
  145
+		};
124 146
 		C6859EA2029092E104C91782 /* Documentation */ = {
125 147
 			isa = PBXGroup;
126 148
 			children = (
@@ -175,7 +197,14 @@
175 197
 			isa = PBXProject;
176 198
 			buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "CHCSVParser" */;
177 199
 			compatibilityVersion = "Xcode 3.1";
  200
+			developmentRegion = English;
178 201
 			hasScannedForEncodings = 1;
  202
+			knownRegions = (
  203
+				English,
  204
+				Japanese,
  205
+				French,
  206
+				German,
  207
+			);
179 208
 			mainGroup = 08FB7794FE84155DC02AAC07 /* CHCSVParser */;
180 209
 			projectDirPath = "";
181 210
 			projectRoot = "";
@@ -222,6 +251,8 @@
222 251
 				557FD0591204A71C009FCDBA /* NSArray+CHCSVAdditions.m in Sources */,
223 252
 				557FD05D1204A731009FCDBA /* UnitTests.m in Sources */,
224 253
 				55EFC7B91210608C0070B303 /* CHCSVWriter.m in Sources */,
  254
+				5516BCB512578CFC0025F235 /* NSString+CHCSVAdditions.m in Sources */,
  255
+				5516BCB912578D750025F235 /* CHCSVSupport.m in Sources */,
225 256
 			);
226 257
 			runOnlyForDeploymentPostprocessing = 0;
227 258
 		};
@@ -232,6 +263,8 @@
232 263
 				8DD76F9A0486AA7600D96B5E /* main.m in Sources */,
233 264
 				551981D61203715400FBE033 /* CHCSVParser.m in Sources */,
234 265
 				557FD0431204A45D009FCDBA /* NSArray+CHCSVAdditions.m in Sources */,
  266
+				5516BCBB12578EA90025F235 /* NSString+CHCSVAdditions.m in Sources */,
  267
+				5516BCBC12578EAD0025F235 /* CHCSVSupport.m in Sources */,
235 268
 			);
236 269
 			runOnlyForDeploymentPostprocessing = 0;
237 270
 		};
21  CHCSVSupport.h
... ...
@@ -0,0 +1,21 @@
  1
+//
  2
+//  CHCSVSupport.h
  3
+//  CHCSVParser
  4
+//
  5
+//  Created by Dave DeLong on 10/2/10.
  6
+//  Copyright 2010 Home. All rights reserved.
  7
+//
  8
+
  9
+#import <Foundation/Foundation.h>
  10
+#import "CHCSVParser.h"
  11
+
  12
+@interface NSArrayCHCSVAggregator : NSObject <CHCSVParserDelegate> {
  13
+	NSMutableArray * lines;
  14
+	NSMutableArray * currentLine;
  15
+	NSError * error;
  16
+}
  17
+
  18
+@property (readonly) NSArray * lines;
  19
+@property (readonly) NSError * error;
  20
+
  21
+@end
47  CHCSVSupport.m
... ...
@@ -0,0 +1,47 @@
  1
+//
  2
+//  CHCSVSupport.m
  3
+//  CHCSVParser
  4
+//
  5
+//  Created by Dave DeLong on 10/2/10.
  6
+//  Copyright 2010 Home. All rights reserved.
  7
+//
  8
+
  9
+#import "CHCSVSupport.h"
  10
+
  11
+
  12
+@implementation NSArrayCHCSVAggregator
  13
+@synthesize lines, error;
  14
+
  15
+- (void) dealloc {
  16
+	[lines release];
  17
+	[currentLine release];
  18
+	[error release];
  19
+	[super dealloc];
  20
+}
  21
+
  22
+- (void) parser:(CHCSVParser *)parser didStartDocument:(NSString *)csvFile {
  23
+	lines = [[NSMutableArray alloc] init];
  24
+}
  25
+
  26
+- (void) parser:(CHCSVParser *)parser didStartLine:(NSUInteger)lineNumber {
  27
+	currentLine = [[NSMutableArray alloc] init];
  28
+}
  29
+
  30
+- (void) parser:(CHCSVParser *)parser didEndLine:(NSUInteger)lineNumber {
  31
+	[lines addObject:currentLine];
  32
+	[currentLine release], currentLine = nil;
  33
+}
  34
+
  35
+- (void) parser:(CHCSVParser *)parser didReadField:(NSString *)field {
  36
+	[currentLine addObject:field];
  37
+}
  38
+
  39
+- (void) parser:(CHCSVParser *)parser didEndDocument:(NSString *)csvFile {
  40
+	
  41
+}
  42
+
  43
+- (void) parser:(CHCSVParser *)parser didFailWithError:(NSError *)anError {
  44
+	error = [anError retain];
  45
+}
  46
+
  47
+@end
11  CHCSVWriter.m
@@ -105,18 +105,19 @@ - (void) closeFile {
105 105
 		[outputHandle release], outputHandle = nil;
106 106
 		
107 107
 		if (atomically == YES && [handleFile isEqual:destinationFile] == NO) {
  108
+			NSError *err = nil;
108 109
 			if ([[NSFileManager defaultManager] fileExistsAtPath:destinationFile]) {
109  
-				NSError *err = nil;
110 110
 				[[NSFileManager defaultManager] removeItemAtPath:destinationFile error:&err];
111 111
 				if (err != nil) {
112 112
 					error = [err retain];
113 113
 					return;
114 114
 				}
115  
-				[[NSFileManager defaultManager] moveItemAtPath:handleFile toPath:destinationFile error:&err];
116  
-				if (err != nil) {
117  
-					error = [err retain];
118  
-				}
119 115
 			}
  116
+			[[NSFileManager defaultManager] moveItemAtPath:handleFile toPath:destinationFile error:&err];
  117
+			if (err != nil) {
  118
+				error = [err retain];
  119
+			}
  120
+			[[NSFileManager defaultManager] removeItemAtPath:handleFile error:nil];
120 121
 		}
121 122
 	}
122 123
 }
56  NSArray+CHCSVAdditions.m
@@ -26,56 +26,7 @@ of this software and associated documentation files (the "Software"), to deal
26 26
 #import "NSArray+CHCSVAdditions.h"
27 27
 #import "CHCSVParser.h"
28 28
 #import "CHCSVWriter.h"
29  
-
30  
-@interface NSArrayCHCSVAggregator : NSObject <CHCSVParserDelegate> {
31  
-	NSMutableArray * lines;
32  
-	NSMutableArray * currentLine;
33  
-	NSError * error;
34  
-}
35  
-
36  
-@property (readonly) NSArray * lines;
37  
-@property (readonly) NSError * error;
38  
-
39  
-@end
40  
-
41  
-@implementation NSArrayCHCSVAggregator
42  
-@synthesize lines, error;
43  
-
44  
-- (void) dealloc {
45  
-	[lines release];
46  
-	[currentLine release];
47  
-	[error release];
48  
-	[super dealloc];
49  
-}
50  
-
51  
-- (void) parser:(CHCSVParser *)parser didStartDocument:(NSString *)csvFile {
52  
-	lines = [[NSMutableArray alloc] init];
53  
-}
54  
-
55  
-- (void) parser:(CHCSVParser *)parser didStartLine:(NSUInteger)lineNumber {
56  
-	currentLine = [[NSMutableArray alloc] init];
57  
-}
58  
-
59  
-- (void) parser:(CHCSVParser *)parser didEndLine:(NSUInteger)lineNumber {
60  
-	[lines addObject:currentLine];
61  
-	[currentLine release], currentLine = nil;
62  
-}
63  
-
64  
-- (void) parser:(CHCSVParser *)parser didReadField:(NSString *)field {
65  
-	[currentLine addObject:field];
66  
-}
67  
-
68  
-- (void) parser:(CHCSVParser *)parser didEndDocument:(NSString *)csvFile {
69  
-
70  
-}
71  
-
72  
-- (void) parser:(CHCSVParser *)parser didFailWithError:(NSError *)anError {
73  
-	error = [anError retain];
74  
-}
75  
-
76  
-@end
77  
-
78  
-
  29
+#import "CHCSVSupport.h"
79 30
 
80 31
 @implementation NSArray (CHCSVAdditions)
81 32
 
@@ -149,13 +100,14 @@ - (BOOL) writeToCSVFile:(NSString *)csvFile atomically:(BOOL)atomically {
149 100
 	
150 101
 	CHCSVWriter * writer = [[CHCSVWriter alloc] initWithCSVFile:csvFile atomic:atomically];
151 102
 	for (NSArray * row in self) {
152  
-		for (NSArray * field in row) {
153  
-			[writer writeField:field];
  103
+		for (id field in row) {
  104
+			[writer writeField:[field description]];
154 105
 		}
155 106
 		[writer writeLine];
156 107
 	}
157 108
 	
158 109
 	ok = ([writer error] == nil);
  110
+	[writer closeFile];
159 111
 	[writer release];
160 112
 	
161 113
 	return ok;
16  NSString+CHCSVAdditions.h
... ...
@@ -0,0 +1,16 @@
  1
+//
  2
+//  NSString+CHCSVAdditions.h
  3
+//  CHCSVParser
  4
+//
  5
+//  Created by Dave DeLong on 10/2/10.
  6
+//  Copyright 2010 Home. All rights reserved.
  7
+//
  8
+
  9
+#import <Foundation/Foundation.h>
  10
+
  11
+
  12
+@interface NSString (CHCSVAdditions)
  13
+
  14
+- (NSArray *) CSVComponents;
  15
+
  16
+@end
32  NSString+CHCSVAdditions.m
... ...
@@ -0,0 +1,32 @@
  1
+//
  2
+//  NSString+CHCSVAdditions.m
  3
+//  CHCSVParser
  4
+//
  5
+//  Created by Dave DeLong on 10/2/10.
  6
+//  Copyright 2010 Home. All rights reserved.
  7
+//
  8
+
  9
+#import "NSString+CHCSVAdditions.h"
  10
+#import "CHCSVParser.h"
  11
+#import "CHCSVSupport.h"
  12
+
  13
+@implementation NSString (CHCSVAdditions)
  14
+
  15
+- (NSArray *) CSVComponents {
  16
+	
  17
+	CHCSVParser * parser = [[CHCSVParser alloc] initWithCSVString:self encoding:[self fastestEncoding] error:nil];
  18
+	NSArrayCHCSVAggregator * delegate = [[NSArrayCHCSVAggregator alloc] init];
  19
+	[parser setParserDelegate:delegate];
  20
+	[parser parse];
  21
+	[parser release];
  22
+	
  23
+	NSArray * results = nil;
  24
+	if ([parser error] == nil) {
  25
+		results = [[[delegate lines] retain] autorelease];
  26
+	}
  27
+	[delegate release];
  28
+	
  29
+	return results;
  30
+}
  31
+
  32
+@end
22  README.markdown
Source Rendered
@@ -13,6 +13,8 @@
13 13
 ###Parsing
14 14
 In order to parse CSV files, you'll need `CHCSVParser.h` and `CHCSVParser.m`.  A `CHCSVParser` works very similarly to an `NSXMLParser`, in that it synchronously parses the data and invokes delegate callback methods to let you know that it has found a field, or has finished reading a line, or has encountered a syntax error.
15 15
 
  16
+A `CHCSVParser` can be created either with a path to a CSV file, or with an `NSString` of CSV data.
  17
+
16 18
 ###Writing
17 19
 In order to write data to a CSV file, you'll need `CHCSVWriter.h` and `CHCSVWriter.m`.  A `CHCSVWriter` has 2 primary methods (beyond the designated initializer): `writeField:` and `writeLine`.
18 20
 
@@ -36,6 +38,26 @@ All of the initializers (both class and instance versions) return an `NSArray` o
36 38
 
37 39
 The `writeToCSVFile:` method expects the same structure (an `NSArray` of `NSArray` objects).
38 40
 
  41
+There is also an `NSString` category to parse an `NSString` of CSV data into an `NSArray` of `NSArray` objects.  This method is:
  42
+
  43
+- `- (NSArray *) CSVComponents;`
  44
+
  45
+Both the `NSArray` and `NSString` categories require including the `CHCSVSupport.h` and `CHCSVSupport.m` files in your project.
  46
+
  47
+###General Use
  48
+
  49
+The simplest use of `CHCSVParser` is to include all of the files in your project:
  50
+
  51
+- `CHCSV.h`
  52
+- `CHCSVParser.h` and `CHCSVParser.m`
  53
+- `CHCSVWriter.h` and `CHCSVWriter.m`
  54
+- `NSArray+CHCSVAdditions.h` and `NSArray+CHCSVAdditions.m`
  55
+- `NSString+CHCSVAdditions.h` and `NSString+CHCSVAdditions.m`
  56
+- `CHCSVSupport.h` and `CHCSVSupport.m`
  57
+
  58
+Then to use any of the CSV parsing or writing functionality, simply `#import "CHCSV.h"` and use any of the classes and categories as you'd like.
  59
+
  60
+
39 61
 ##Data Encoding
40 62
 `CHCSVParser` relies on knowing the encoding of the CSV file.  It should work with pretty much any kind of file encoding, if you can provide what that encoding is.  If you do not know the encoding of the file, then `CHCSVParser` can make a naïve guess.  `CHCSVParser` will try to guess the encoding of the file from among these options:
41 63
 
2  UnitTests.h
@@ -27,7 +27,7 @@
27 27
 
28 28
 
29 29
 @interface UnitTests : SenTestCase {
30  
-
  30
+	NSAutoreleasePool * testPool;
31 31
 }
32 32
 
33 33
 @end
69  UnitTests.m
@@ -25,9 +25,37 @@ of this software and associated documentation files (the "Software"), to deal
25 25
 
26 26
 #import "UnitTests.h"
27 27
 #import "NSArray+CHCSVAdditions.h"
  28
+#import "NSString+CHCSVAdditions.h"
28 29
 
29 30
 @implementation UnitTests
30 31
 
  32
+- (void) setUp {
  33
+	testPool = [[NSAutoreleasePool alloc] init];
  34
+}
  35
+
  36
+- (void) tearDown {
  37
+	[testPool drain], testPool = nil;
  38
+}
  39
+
  40
+- (NSArray *) expectedFields {
  41
+	return [NSArray arrayWithObjects:
  42
+			[NSArray arrayWithObjects:@"This",@"is",@"a",@"simple",@"line",nil],
  43
+			[NSArray arrayWithObjects:@"This",@"is",@"a",@"quoted",@"line",nil],
  44
+			[NSArray arrayWithObjects:@"This",@"is",@"a",@"mixed",@"line",nil],
  45
+			[NSArray arrayWithObjects:@"This",@"has",@"a\nmultiline\nfield",nil],
  46
+			[NSArray arrayWithObjects:@"#This",@"line",@"should",@"not",@"be",@"ignored",nil],
  47
+			[NSArray arrayWithObjects:@"This",@"has",@"\"escaped\"",@"quotes",nil],
  48
+			[NSArray arrayWithObjects:@"This",@"has",@"\"escaped\"",@"quotes",nil],
  49
+			[NSArray arrayWithObjects:@"This",@"has",@"empty",@"fields",@"",@"",@"",nil],
  50
+			[NSArray arrayWithObjects:@"This",@"has",@"escaped",@"escapes\\",nil],
  51
+			[NSArray arrayWithObjects:@"This",@"has",@"escaped",@"commas,",nil],
  52
+			[NSArray arrayWithObjects:@"This",@"has",@"quoted",@"commas,",nil],
  53
+			[NSArray arrayWithObjects:@"This",@"has",@"empty",@"quoted",@"fields",@"",@"",nil],
  54
+			[NSArray arrayWithObjects:@"This",@"has",@"mixed",@"\"escaped quotes\"", nil],
  55
+			[NSArray arrayWithObjects:@"This",@"is",@"the",@"last",@"line",nil],
  56
+			nil];
  57
+}
  58
+
31 59
 - (void) testCSV {
32 60
 	NSString * file = [[NSBundle bundleForClass:[self class]] pathForResource:@"Test" ofType:@"csv"];
33 61
 	
@@ -38,22 +66,7 @@ - (void) testCSV {
38 66
 	STAssertTrue(encoding == NSUTF8StringEncoding, @"Wrong encoding; given %@ (%lu)", CFStringGetNameOfEncoding(CFStringConvertNSStringEncodingToEncoding(encoding)), encoding);
39 67
 	STAssertNil(error, @"Unexpected error: %@", error);
40 68
 	
41  
-	NSArray * expectedFields = [NSArray arrayWithObjects:
42  
-								[NSArray arrayWithObjects:@"This",@"is",@"a",@"simple",@"line",nil],
43  
-								[NSArray arrayWithObjects:@"This",@"is",@"a",@"quoted",@"line",nil],
44  
-								[NSArray arrayWithObjects:@"This",@"is",@"a",@"mixed",@"line",nil],
45  
-								[NSArray arrayWithObjects:@"This",@"has",@"a\nmultiline\nfield",nil],
46  
-								[NSArray arrayWithObjects:@"#This",@"line",@"should",@"not",@"be",@"ignored",nil],
47  
-								[NSArray arrayWithObjects:@"This",@"has",@"\"escaped\"",@"quotes",nil],
48  
-								[NSArray arrayWithObjects:@"This",@"has",@"\"escaped\"",@"quotes",nil],
49  
-								[NSArray arrayWithObjects:@"This",@"has",@"empty",@"fields",@"",@"",@"",nil],
50  
-								[NSArray arrayWithObjects:@"This",@"has",@"escaped",@"escapes\\",nil],
51  
-								[NSArray arrayWithObjects:@"This",@"has",@"escaped",@"commas,",nil],
52  
-								[NSArray arrayWithObjects:@"This",@"has",@"quoted",@"commas,",nil],
53  
-								[NSArray arrayWithObjects:@"This",@"has",@"empty",@"quoted",@"fields",@"",@"",nil],
54  
-								[NSArray arrayWithObjects:@"This",@"has",@"mixed",@"\"escaped quotes\"", nil],
55  
-								[NSArray arrayWithObjects:@"This",@"is",@"the",@"last",@"line",nil],
56  
-								nil];	
  69
+	NSArray * expectedFields = [self expectedFields];
57 70
 	
58 71
 	NSUInteger expectedCount = [expectedFields count];
59 72
 	NSUInteger actualCount = [fields count];
@@ -65,7 +78,8 @@ - (void) testCSV {
65 78
 		STAssertTrue([actualLine isEqualToArray:expectedLine], @"lines differ.  Expected %@, given %@", expectedLine, actualLine);
66 79
 	}
67 80
 	
68  
-	NSString * tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:@"test.csv"];
  81
+	NSString * tempFileName = [NSString stringWithFormat:@"%d-test.csv", arc4random()];
  82
+	NSString * tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:tempFileName];
69 83
 	NSLog(@"Writing to file: %@", tempFile);
70 84
 	BOOL writtenToFile = [expectedFields writeToCSVFile:tempFile atomically:YES];
71 85
 	
@@ -87,4 +101,25 @@ - (void) testCSV {
87 101
 	}
88 102
 }
89 103
 
  104
+- (void) testCSVString {
  105
+	NSString * file = [[NSBundle bundleForClass:[self class]] pathForResource:@"Test" ofType:@"csv"];
  106
+	
  107
+	NSStringEncoding encoding = 0;
  108
+	NSString * csv = [NSString stringWithContentsOfFile:file usedEncoding:&encoding error:nil];
  109
+	NSArray * fields = [csv CSVComponents];
  110
+	NSLog(@"fields: %@", fields);
  111
+	
  112
+	NSArray * expectedFields = [self expectedFields];
  113
+	
  114
+	NSUInteger expectedCount = [expectedFields count];
  115
+	NSUInteger actualCount = [fields count];
  116
+	STAssertTrue(expectedCount == actualCount, @"incorrect number of lines parsed.  expected %lu, given %lu", expectedCount, actualCount);
  117
+	for (int i = 0; i < MIN(expectedCount, actualCount); ++i) {
  118
+		NSArray * actualLine = [fields objectAtIndex:i];
  119
+		NSArray * expectedLine = [expectedFields objectAtIndex:i];
  120
+		
  121
+		STAssertTrue([actualLine isEqualToArray:expectedLine], @"lines differ.  Expected %@, given %@", expectedLine, actualLine);
  122
+	}
  123
+}
  124
+
90 125
 @end

0 notes on commit 7cba09d

Please sign in to comment.
Something went wrong with that request. Please try again.