From 448f98810d5f6a69da136e86f67fa1d4823c8b91 Mon Sep 17 00:00:00 2001 From: kohkimakimoto Date: Mon, 9 Jun 2014 15:05:48 +0900 Subject: [PATCH] Develop csv parser --- FMDBx.xcodeproj/project.pbxproj | 14 +++++ FMDBx/Classes/FMXCsvTable.h | 10 ++-- FMDBx/Classes/FMXCsvTable.m | 76 +++++++++++++++++++++++++-- FMDBxTests/Classes/FMXCsvTableTests.m | 15 +++++- FMDBxTests/Classes/users.csv | 8 +++ 5 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 FMDBxTests/Classes/users.csv diff --git a/FMDBx.xcodeproj/project.pbxproj b/FMDBx.xcodeproj/project.pbxproj index e54aa48..37f3232 100644 --- a/FMDBx.xcodeproj/project.pbxproj +++ b/FMDBx.xcodeproj/project.pbxproj @@ -37,6 +37,8 @@ C25B3DF91936D46600174E72 /* FMXQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C25B3DF81936D46600174E72 /* FMXQueryTests.m */; }; C2935322194569F5003D2E6B /* FMXCsvTable.m in Sources */ = {isa = PBXBuildFile; fileRef = C2935321194569F5003D2E6B /* FMXCsvTable.m */; }; C293532419456A18003D2E6B /* FMXCsvTableTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C293532319456A18003D2E6B /* FMXCsvTableTests.m */; }; + C29353271945839B003D2E6B /* users.csv in Resources */ = {isa = PBXBuildFile; fileRef = C29353261945839B003D2E6B /* users.csv */; }; + C293532819458484003D2E6B /* users.csv in Resources */ = {isa = PBXBuildFile; fileRef = C29353261945839B003D2E6B /* users.csv */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -97,6 +99,7 @@ C2935320194569F5003D2E6B /* FMXCsvTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMXCsvTable.h; sourceTree = ""; }; C2935321194569F5003D2E6B /* FMXCsvTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMXCsvTable.m; sourceTree = ""; }; C293532319456A18003D2E6B /* FMXCsvTableTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMXCsvTableTests.m; sourceTree = ""; }; + C29353261945839B003D2E6B /* users.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = users.csv; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -227,6 +230,7 @@ C254D2461931807C0040AE69 /* Classes */ = { isa = PBXGroup; children = ( + C293532519458384003D2E6B /* CSV */, C25B3DA719321B7000174E72 /* Models */, C25B3DA01931905200174E72 /* Migration */, C25B3DA3193219ED00174E72 /* FMXDatabaseConfigurationTests.m */, @@ -257,6 +261,14 @@ name = Models; sourceTree = ""; }; + C293532519458384003D2E6B /* CSV */ = { + isa = PBXGroup; + children = ( + C29353261945839B003D2E6B /* users.csv */, + ); + name = CSV; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -335,6 +347,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + C293532819458484003D2E6B /* users.csv in Resources */, C254D20E19317EDC0040AE69 /* InfoPlist.strings in Resources */, C254D21619317EDC0040AE69 /* Images.xcassets in Resources */, ); @@ -345,6 +358,7 @@ buildActionMask = 2147483647; files = ( C254D22719317EDC0040AE69 /* InfoPlist.strings in Resources */, + C29353271945839B003D2E6B /* users.csv in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/FMDBx/Classes/FMXCsvTable.h b/FMDBx/Classes/FMXCsvTable.h index 494b013..3b15c2d 100644 --- a/FMDBx/Classes/FMXCsvTable.h +++ b/FMDBx/Classes/FMXCsvTable.h @@ -1,5 +1,5 @@ // -// FMXCsv.h +// FMXCsvTable.h // FMDBx // // Created by KohkiMakimoto on 6/9/14. @@ -10,12 +10,12 @@ @interface FMXCsvTable : NSObject -+ (void)foreachFileName:(NSString *)fileName process:(void (^)(NSArray *row))process; ++ (void)foreachFileName:(NSString *)fileName process:(void (^)(NSDictionary *row))process; -+ (void)foreachFileName:(NSString *)fileName columnSeparator:(NSString *)separator process:(void (^)(NSArray *row))process; ++ (void)foreachFileName:(NSString *)fileName columnSeparator:(NSString *)separator process:(void (^)(NSDictionary *row))process; -+ (void)foreachURL:(NSURL *)url process:(void (^)(NSArray *row))process; ++ (void)foreachURL:(NSURL *)url process:(void (^)(NSDictionary *row))process; -+ (void)foreachURL:(NSURL *)url columnSeparator:(NSString *)separator process:(void (^)(NSArray *row))process; ++ (void)foreachURL:(NSURL *)url columnSeparator:(NSString *)separator process:(void (^)(NSDictionary *row))process; @end diff --git a/FMDBx/Classes/FMXCsvTable.m b/FMDBx/Classes/FMXCsvTable.m index 63a0f59..b93cf45 100644 --- a/FMDBx/Classes/FMXCsvTable.m +++ b/FMDBx/Classes/FMXCsvTable.m @@ -1,5 +1,5 @@ // -// FMXCsv.m +// FMXCsvTable.m // FMDBx // // Created by KohkiMakimoto on 6/9/14. @@ -37,14 +37,17 @@ #import "FMXCsvTable.h" +static NSCharacterSet *FMXCsvTableDigitCharacterSet = nil; +static NSArray *FMXCsvTableBooleanStrings = nil; + @implementation FMXCsvTable -+ (void)foreachFileName:(NSString *)fileName process:(void (^)(NSArray *))process ++ (void)foreachFileName:(NSString *)fileName process:(void (^)(NSDictionary *))process { [self foreachFileName:fileName columnSeparator:@"," process:process]; } -+ (void)foreachFileName:(NSString *)fileName columnSeparator:(NSString *)separator process:(void (^)(NSArray *))process ++ (void)foreachFileName:(NSString *)fileName columnSeparator:(NSString *)separator process:(void (^)(NSDictionary *))process { NSBundle *bundle = [NSBundle mainBundle]; NSString *path = [bundle pathForResource:fileName ofType:nil]; @@ -52,16 +55,79 @@ + (void)foreachFileName:(NSString *)fileName columnSeparator:(NSString *)separat [self foreachURL:url columnSeparator:separator process:process]; } -+ (void)foreachURL:(NSURL *)url process:(void (^)(NSArray *))process ++ (void)foreachURL:(NSURL *)url process:(void (^)(NSDictionary *))process { [self foreachURL:url columnSeparator:@"," process:process]; } -+ (void)foreachURL:(NSURL *)url columnSeparator:(NSString *)separator process:(void (^)(NSArray *))process ++ (void)foreachURL:(NSURL *)url columnSeparator:(NSString *)separator process:(void (^)(NSDictionary *))process { NSString *csvString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; csvString = [csvString stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + NSArray *lines = [csvString componentsSeparatedByString:@"\n"]; + NSArray *headers = [self headersFromLines:lines columnSeparator:separator]; + NSArray *rows = [self rowsFromLines:lines headers:headers columnSeparator:separator]; + + for (NSDictionary *row in rows) { + process(row); + } +} + +# pragma mark - private methods + ++ (NSArray *)headersFromLines:(NSArray *)lines columnSeparator:(NSString *)separator +{ + NSString *headerLine = lines.firstObject; + return [headerLine componentsSeparatedByString:separator]; +} + ++ (NSArray *)rowsFromLines:(NSArray *)lines headers:(NSArray *)headers columnSeparator:(NSString *)separator +{ + NSMutableArray *rows = [NSMutableArray new]; + for (NSString *line in lines) { + NSInteger lineNumber = [lines indexOfObject:line]; + if (lineNumber == 0) { + continue; + } + + NSArray *values = [line componentsSeparatedByString:separator]; + NSMutableDictionary *row = [NSMutableDictionary new]; + for (NSString *header in headers) { + NSUInteger index = [headers indexOfObject:header]; + NSString *value = values[index]; + if ([self isDigit:value]) { + row[header] = [NSNumber numberWithLongLong:value.longLongValue]; + } else if ([self isBoolean:value]) { + row[header] = [NSNumber numberWithBool:value.boolValue]; + } else { + row[header] = values[index]; + } + } + [rows addObject:[NSDictionary dictionaryWithDictionary:row]]; + } + return [NSArray arrayWithArray:rows]; +} + ++ (BOOL)isDigit:(NSString *)string +{ + if (!FMXCsvTableDigitCharacterSet) { + FMXCsvTableDigitCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"]; + } + + NSScanner *scanner = [NSScanner localizedScannerWithString:string]; + scanner.charactersToBeSkipped = NO; + [scanner scanCharactersFromSet:FMXCsvTableDigitCharacterSet intoString:NULL]; + return scanner.isAtEnd; +} + ++ (BOOL)isBoolean:(NSString *)string +{ + if (!FMXCsvTableBooleanStrings) { + FMXCsvTableBooleanStrings = @[@"YES", @"NO", @"yes", @"no", @"TRUE", @"FALSE", @"true", @"false"]; + } + + return [FMXCsvTableBooleanStrings containsObject:string]; } @end diff --git a/FMDBxTests/Classes/FMXCsvTableTests.m b/FMDBxTests/Classes/FMXCsvTableTests.m index f3f099d..8f42155 100644 --- a/FMDBxTests/Classes/FMXCsvTableTests.m +++ b/FMDBxTests/Classes/FMXCsvTableTests.m @@ -1,5 +1,5 @@ // -// FMXCsvTests.m +// FMXCsvTableTests.m // FMDBx // // Created by KohkiMakimoto on 6/9/14. @@ -32,6 +32,19 @@ - (void)tearDown - (void)testForeachFileName { + [FMXCsvTable foreachFileName:@"users.csv" columnSeparator:@"," process:^(NSDictionary *row){ + NSNumber *idNumber = (NSNumber *)[row objectForKey:@"id"]; + if ([idNumber isEqualToNumber:@(1)]) { + XCTAssertEqualObjects(@"Kohki Makimoto1", [row objectForKey:@"name"]); + XCTAssertEqualObjects(@(34), [row objectForKey:@"age"]); + } + + if ([idNumber isEqualToNumber:@(2)]) { + XCTAssertEqualObjects(@"Kohki Makimoto2", [row objectForKey:@"name"]); + XCTAssertEqualObjects(@(35), [row objectForKey:@"age"]); + } + + }]; } - (void)testForeachURL diff --git a/FMDBxTests/Classes/users.csv b/FMDBxTests/Classes/users.csv new file mode 100644 index 0000000..b866eff --- /dev/null +++ b/FMDBxTests/Classes/users.csv @@ -0,0 +1,8 @@ +id,name,age +1,Kohki Makimoto1,34 +2,Kohki Makimoto2,35 +3,Kohki Makimoto3,36 +4,Kohki Makimoto4,37 +5,Kohki Makimoto5,38 +6,Kohki Makimoto6,39 +7,Kohki Makimoto7,40