czwartek, 28 listopada 2013


Witam,

Dzisiaj szybkie wejście w parsowanie plików csv. W przykładzie posłużę się plikiem zawierającym informacje na temat miast (nazwa, kraj, współrzędne itd.). Do czego oczekujemy tabela z opcją wyszukiwania - czyli prototyp aplikacji.





przykład rekordu:

10741,US,GA,Omega,31775,31.3274,-83.6051,525,229

Dla celów "edukacyjny" :) zrobiłem na szybko projekt w którym plik csv parsujemy przy użyciu klasy CHCSVParser dostepny:

https://github.com/davedelong/CHCSVParser

Nastepnie tworzymy nowy model danych CDCityData, który wygląda nastepująco:

@interface CDCityData : NSObject
@property (nonatomic, retain) NSString * country;
@property (nonatomic, retain) NSString * region;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSString * postal_code;
@property (nonatomic) double lat;
@property (nonatomic) double lon;
@property (nonatomic, retain) NSString * area_code;
- (void) fillWithArray:(NSArray*) array;

@end

Z implementacji metody fillWithArray skorzystamy do uzupełnienia modelu danymi z pliku csv.

W ostatnim kroku wyświetlimy dane przy użyciu UITableView oraz dodatkowo dorzuciłem możliwość wyszukiwania na liście miasta ( UISearchBar ) po nazwie.

OK, Do dzieła.


1. Wczytanie i parsowanie pliku csv:

        NSString *file = [[NSBundle bundleForClass:[self class]] pathForResource:@"GeoLiteCity-Location" ofType:@"csv"];
        NSArray *readFromFile = [NSArray arrayWithContentsOfCSVFile:file];

W CHCSVParser znajduje się kategoria NSArray której fragment poniżej:

@implementation NSArray (CHCSVAdditions)

+ (instancetype)arrayWithContentsOfCSVFile:(NSString *)csvFilePath {
    return [self arrayWithContentsOfCSVFile:csvFilePath options:0];

}

Dzięki temu możemy w szybki i łatwy sposób otrzymać tablice już z gotowymi elementami z kazdego rekordu. Dla osób chcących zgłębić temat, proponuje zajrzeć do implementacji klasy CHCSVParser.



2. Tworzenie modelu danych CDCityData

  for (NSArray *array in readFromFile) {
            CDCityData *cityData = [[CDCityData alloc] init];
            [cityData fillWithArray:array];
            [self.orginalData addObject:cityData];

        }

- (void) fillWithArray:(NSArray*) array{
    self.country = [array objectAtIndex:0];
    self.region = [array objectAtIndex:1];
    self.name = [array objectAtIndex:3];
    self.postal_code = [array objectAtIndex:4];
    self.lat = [[array objectAtIndex:5] doubleValue];
    self.lon = [[array objectAtIndex:6] doubleValue];
}

Myślę że to nie wymaga komentarza:), oczywiście warto dodać warunki na nsarray z którego budujemy model, mnie się już nie chciało musicie wybaczyć:)

3. Parsowanie vs Main Thread

Zapewne staje się oczywiste, iż całej operacji nie powinniśmy wykonywać na głównym wątku. Myśle, że osoby które już miały do czynienia z projektami z użyciem UITableView wiedzą o co chodzi. Najpierw prosze abyście zerknęli poniżej na całość kodu znajdującego sie w głownym view controlerze.

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"START PARSING");
    [self.indicatorView startAnimating];
    self.orginalData = [NSMutableArray array];
    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();

    dispatch_queue_t myQueue = dispatch_queue_create("com.parsing.csv", NULL);
    dispatch_async(myQueue, ^{
        NSString *file = [[NSBundle bundleForClass:[self class]] pathForResource:@"GeoLiteCity-Location" ofType:@"csv"];
        NSArray *readFromFile = [NSArray arrayWithContentsOfCSVFile:file];
        for (NSArray *array in readFromFile) {
            CDCityData *cityData = [[CDCityData alloc] init];
            [cityData fillWithArray:array];
            [self.orginalData addObject:cityData];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.indicatorView stopAnimating];

            self.visibleData = [self.orginalData mutableCopy];
            [self.tableView reloadData];

            NSLog(@"END PARSING = %f", CFAbsoluteTimeGetCurrent()-startTime);
        });
    });

}

Jak widzimy utworzona została kolejka myQueue, z której skorzystałem do przeniesienia operacji parsowania. Kod wykonuje się w tej sekcji asynchronicznie, wiec gdy tylko już cały model zostanie przeparsowany wchodzę z powrotem w wątek główny i  wyświetlam dane na tablicy. To wszystko w mega dużym skrócie. Dla chętnych polecam odpalenie aplikacji z użyciem Profiler'a w trybie Time Profiler.




Do projektu dorzucony jest jeszcze search, dla chętnych można zaciągnać i sprawdzić z mojego prywatnego konta na githubie.


https://github.com/fenk/CSVParsingTest.git

Dla dociekliwych:

Sprawdzcie ile zajmuje parsowanie takiego pliku na urządzeniu (nie symulatorze), jeżeli macie pomysł na usprawnienie parsowania/ poprawienie szybkości działania programu, chetnie czekam na komentarze!

Pozdrawiam!


0 komentarze:

Prześlij komentarz