【问题标题】:Loading csv file in Core Data在 Core Data 中加载 csv 文件
【发布时间】:2013-09-26 21:52:02
【问题描述】:

第一次运行应用程序时,我试图在核心数据中加载一个 csv 文件。在此处(What is the fastest way to load a large CSV file into core data)找到的关于 stackoverflow 的另一篇文章中,我发现了如何做到这一点。

我在我的控制器中使用与提供的函数相同的代码:populateDB,如果之前从未加载过数据(第一次运行),则调用该函数。但是,xcode 给了我一个错误:

No visible @interface for ...Controller declares the selector persistentStoreCoordinator.

函数是:

-(void)populateDB{
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
NSManagedObjectContext *context;
if (coordinator != nil) {
    context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:coordinator];
}

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"input" ofType:@"txt"];  
if (filePath) {  
NSString * myText = [[NSString alloc]
                           initWithContentsOfFile:filePath
                           encoding:NSUTF8StringEncoding
                           error:nil];
if (myText) {
    __block int count = 0;


    [myText enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) {
        line=[line stringByReplacingOccurrencesOfString:@"\t" withString:@" "];
        NSArray *lineComponents=[line componentsSeparatedByString:@" "];
        if(lineComponents){
            if([lineComponents count]==3){
                float f=[[lineComponents objectAtIndex:0] floatValue];
                NSNumber *number=[NSNumber numberWithFloat:f];
                NSString *string1=[lineComponents objectAtIndex:1];
                NSString *string2=[lineComponents objectAtIndex:2];
                NSManagedObject *object=[NSEntityDescription insertNewObjectForEntityForName:@"Bigram" inManagedObjectContext:context];
                [object setValue:number forKey:@"number"];
                [object setValue:string1 forKey:@"string1"];
                [object setValue:string2 forKey:@"string2"];
                NSError *error;
                count++;
                if(count>=1000){
                    if (![context save:&error]) {
                        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
                    }
                    count=0;

                }
            }
        }



    }];
    NSLog(@"done importing");
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }

}  
}
}

我在离开 3 年后再次拿起 iOS,我之前从未深入研究过 SDK 的这一部分。我将不胜感激任何有关此问题的帮助...

【问题讨论】:

  • 感谢@GabrielePetronella 在这里的编辑/课程:)
  • 我们又见面了 :) 只是简短的评论,因为我们在这里:您应该发布答案,而不是在问题中包含答案。如果您认为这是最合适的答案,您也可以接受(2 天后)
  • 我应该继续更新吗?
  • 干得好,现在格式完美,人们更容易找到答案。

标签: ios objective-c


【解决方案1】:

下面的代码向您展示了一个在 Core Data 中加载 csv 文件的示例,该示例使用 Matt Gallagher 的 CSVParser.h 类并假设一个实体 MyEntity

    // CSV File input.csv and not input.txt separate by space @" "
    CSVParser *csvParser = [[CSVParser alloc] initWithContentOfFile:[NSBundle mainBundle] pathForResource:@"input" ofType:@"csv" separator:@" "];

    // Array with all your data from CSV
    NSArray *data = [csvParser parseFile];

    // Your entity from Core Data
    MyEntity *myEntity = nil;
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    for (NSDictionary *dic in data)
    {
        fetchRequest.entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:context];

        if (!entity)
            entity = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity"
                                      inManagedObjectContext:context];

        [entity setValue:number forKey:@"number"];
        [entity setValue:string1 forKey:@"string1"];
        [entity setValue:string2 forKey:@"string2"];
    }

    // Save the context
    [managedObjectContext save:nil];

CVSParser.h 使用 ARC:

//
//  CSVParser.h
//  CSVImporter
//
//  Created by Matt Gallagher on 2009/11/30.
//  Copyright 2009 Matt Gallagher. All rights reserved.
//
//  Permission is given to use this source code file, free of charge, in any
//  project, commercial or otherwise, entirely at your risk, with the condition
//  that any redistribution (in part or whole) of source code must retain
//  this copyright and permission notice. Attribution in compiled projects is
//  appreciated but not required.
//
// Source : http://cocoawithlove.com/2009/11/writing-parser-using-nsscanner-csv.html

@interface CSVParser : NSObject
{
    NSString *csvString;
    NSString *separator;
    NSScanner *scanner;
    BOOL hasHeader;
    NSMutableArray *fieldNames;
    id receiver;
    SEL receiverSelector;
    NSCharacterSet *endTextCharacterSet;
    BOOL separatorIsSingleChar;
}

- (id)initWithString:(NSString *)aCSVString
           separator:(NSString *)aSeparatorString;

- (id)initWithContentOfFile:(NSString *)aPath
                  separator:(NSString *)aSeparatorString;

- (id)initWithString:(NSString *)aCSVString
           separator:(NSString *)aSeparatorString
           hasHeader:(BOOL)header
          fieldNames:(NSArray *)names;

- (id)initWithContentOfFile:(NSString *)aPath
                  separator:(NSString *)aSeparatorString
                  hasHeader:(BOOL)header
                 fieldNames:(NSArray *)names;

- (NSArray *)arrayOfParsedRows;
- (void)parseRowsForReceiver:(id)aReceiver selector:(SEL)aSelector;

- (NSArray *)parseFile;
- (NSMutableArray *)parseHeader;
- (NSDictionary *)parseRecord;
- (NSString *)parseName;
- (NSString *)parseField;
- (NSString *)parseEscaped;
- (NSString *)parseNonEscaped;
- (NSString *)parseDoubleQuote;
- (NSString *)parseSeparator;
- (NSString *)parseLineSeparator;
- (NSString *)parseTwoDoubleQuotes;
- (NSString *)parseTextData;

@end

CVSParser.m 使用 ARC:

//
//  CSVParser.m
//  CSVImporter
//
//  Created by Matt Gallagher on 2009/11/30.
//  Copyright 2009 Matt Gallagher. All rights reserved.
//
//  Permission is given to use this source code file, free of charge, in any
//  project, commercial or otherwise, entirely at your risk, with the condition
//  that any redistribution (in part or whole) of source code must retain
//  this copyright and permission notice. Attribution in compiled projects is
//  appreciated but not required.
//

#import "CSVParser.h"


@implementation CSVParser

//
// initWithString:separator:hasHeader:fieldNames:
//
// Parameters:
//    aCSVString - the string that will be parsed
//    aSeparatorString - the separator (normally "," or "\t")
//    header - if YES, treats the first row as a list of field names
//    names - a list of field names (will have no effect if header is YES)
//
// returns the initialized object (nil on failure)
//
- (id)initWithString:(NSString *)aCSVString
    separator:(NSString *)aSeparatorString
    hasHeader:(BOOL)header
    fieldNames:(NSArray *)names
{
    self = [super init];
    if (self)
    {
        csvString = [aCSVString retain];
        separator = [aSeparatorString retain];

        NSAssert([separator length] > 0 &&
            [separator rangeOfString:@"\""].location == NSNotFound &&
            [separator rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location == NSNotFound,
            @"CSV separator string must not be empty and must not contain the double quote character or newline characters.");

        NSMutableCharacterSet *endTextMutableCharacterSet =
            [[NSCharacterSet newlineCharacterSet] mutableCopy];
        [endTextMutableCharacterSet addCharactersInString:@"\""];
        [endTextMutableCharacterSet addCharactersInString:[separator substringToIndex:1]];
        endTextCharacterSet = endTextMutableCharacterSet;

        if ([separator length] == 1)
        {
            separatorIsSingleChar = YES;
        }

        hasHeader = header;
        fieldNames = [names mutableCopy];
    }

    return self;
}

- (id)initWithString:(NSString *)aCSVString
           separator:(NSString *)aSeparatorString
{
    return [self initWithString:aCSVString
                      separator:aSeparatorString
                      hasHeader:YES
                     fieldNames:nil];
}

- (id)initWithContentOfFile:(NSString *)aPath
                  separator:(NSString *)aSeparatorString
{
    return [self initWithString:[NSString stringWithContentsOfFile:aPath 
                                                          encoding:NSUTF8StringEncoding
                                                             error:nil]
                      separator:aSeparatorString];  
}

- (id)initWithContentOfFile:(NSString *)aPath
                  separator:(NSString *)aSeparatorString
                  hasHeader:(BOOL)header
                 fieldNames:(NSArray *)names
{
    return [self initWithString:[NSString stringWithContentsOfFile:aPath 
                                                          encoding:NSUTF8StringEncoding
                                                             error:nil]         
                      separator:aSeparatorString
                      hasHeader:header
                     fieldNames:names]; 
}

//
// dealloc
//
// Releases instance memory.
//
- (void)dealloc
{
    [csvString release];
    [separator release];
    [fieldNames release];
    [endTextCharacterSet release];
    [super dealloc];
}


//
// arrayOfParsedRows
//
// Performs a parsing of the csvString, returning the entire result.
//
// returns the array of all parsed row records
//
- (NSArray *)arrayOfParsedRows
{
    scanner = [[NSScanner alloc] initWithString:csvString];
    [scanner setCharactersToBeSkipped:[[[NSCharacterSet alloc] init] autorelease]];

    NSArray *result = [self parseFile];
    [scanner release];
    scanner = nil;

    return result;
}

//
// parseRowsForReceiver:selector:
//
// Performs a parsing of the csvString, sending the entries, 1 row at a time,
// to the receiver.
//
// Parameters:
//    aReceiver - the target that will receive each row as it is parsed
//    aSelector - the selector that will receive each row as it is parsed
//      (should be a method that takes a single NSDictionary argument)
//
- (void)parseRowsForReceiver:(id)aReceiver selector:(SEL)aSelector
{
    scanner = [[NSScanner alloc] initWithString:csvString];
    [scanner setCharactersToBeSkipped:[[[NSCharacterSet alloc] init] autorelease]];
    receiver = [aReceiver retain];
    receiverSelector = aSelector;

    [self parseFile];

    [scanner release];
    scanner = nil;
    [receiver release];
    receiver = nil;
}

//
// parseFile
//
// Attempts to parse a file from the current scan location.
//
// returns the parsed results if successful and receiver is nil, otherwise
//  returns nil when done or on failure.
//
- (NSArray *)parseFile
{
    scanner = [[NSScanner alloc] initWithString:csvString];
    [scanner setCharactersToBeSkipped:[[[NSCharacterSet alloc] init] autorelease]];

    if (hasHeader)
    {
        if (fieldNames)
        {
            [fieldNames release];
        }

        fieldNames = [[self parseHeader] retain];
        if (!fieldNames || ![self parseLineSeparator])
        {
            return nil;
        }
    }

    NSMutableArray *records = nil;
    if (!receiver)
    {
        records = [NSMutableArray array];
    }

    NSDictionary *record = [[self parseRecord] retain];
    if (!record)
    {
        return nil;
    }

    while (record)
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        if (receiver)
        {
            [receiver performSelector:receiverSelector withObject:record];
        }
        else
        {
            [records addObject:record];
        }
        [record release];

        if (![self parseLineSeparator])
        {
            break;
        }

        record = [[self parseRecord] retain];

        [pool drain];
    }

    [scanner release];
    scanner = nil;

    return records;
}

//
// parseHeader
//
// Attempts to parse a header row from the current scan location.
//
// returns the array of parsed field names or nil on parse failure.
//
- (NSMutableArray *)parseHeader
{
    NSString *name = [self parseName];
    if (!name)
    {
        return nil;
    }

    NSMutableArray *names = [NSMutableArray array];
    while (name)
    {
        [names addObject:name];

        if (![self parseSeparator])
        {
            break;
        }

        name = [self parseName];
    }
    return names;
}

//
// parseRecord
//
// Attempts to parse a record from the current scan location. The record
// dictionary will use the fieldNames as keys, or FIELD_X for each column
// X-1 if no fieldName exists for a given column.
//
// returns the parsed record as a dictionary, or nil on failure. 
//
- (NSDictionary *)parseRecord
{
    //
    // Special case: return nil if the line is blank. Without this special case,
    // it would parse as a single blank field.
    //
    if ([self parseLineSeparator] || [scanner isAtEnd])
    {
        return nil;
    }

    NSString *field = [self parseField];
    if (!field)
    {
        return nil;
    }

    NSInteger fieldNamesCount = [fieldNames count];
    NSInteger fieldCount = 0;

    NSMutableDictionary *record =
        [NSMutableDictionary dictionaryWithCapacity:[fieldNames count]];
    while (field)
    {
        NSString *fieldName;
        if (fieldNamesCount > fieldCount)
        {
            fieldName = [fieldNames objectAtIndex:fieldCount];
        }
        else
        {
            fieldName = [NSString stringWithFormat:@"FIELD_%d", fieldCount + 1];
            [fieldNames addObject:fieldName];
            fieldNamesCount++;
        }

        [record setObject:field forKey:fieldName];
        fieldCount++;

        if (![self parseSeparator])
        {
            break;
        }

        field = [self parseField];
    }

    return record;
}

//
// parseName
//
// Attempts to parse a name from the current scan location.
//
// returns the name or nil.
//
- (NSString *)parseName
{
    return [self parseField];
}

//
// parseField
//
// Attempts to parse a field from the current scan location.
//
// returns the field or nil
//
- (NSString *)parseField
{
    NSString *escapedString = [self parseEscaped];
    if (escapedString)
    {
        return escapedString;
    }

    NSString *nonEscapedString = [self parseNonEscaped];
    if (nonEscapedString)
    {
        return nonEscapedString;
    }

    //
    // Special case: if the current location is immediately
    // followed by a separator, then the field is a valid, empty string.
    //
    NSInteger currentLocation = [scanner scanLocation];
    if ([self parseSeparator] || [self parseLineSeparator] || [scanner isAtEnd])
    {
        [scanner setScanLocation:currentLocation];
        return @"";
    }

    return nil;
}

//
// parseEscaped
//
// Attempts to parse an escaped field value from the current scan location.
//
// returns the field value or nil.
//
- (NSString *)parseEscaped
{
    if (![self parseDoubleQuote])
    {
        return nil;
    }

    NSString *accumulatedData = [NSString string];
    while (YES)
    {
        NSString *fragment = [self parseTextData];
        if (!fragment)
        {
            fragment = [self parseSeparator];
            if (!fragment)
            {
                fragment = [self parseLineSeparator];
                if (!fragment)
                {
                    if ([self parseTwoDoubleQuotes])
                    {
                        fragment = @"\"";
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }

        accumulatedData = [accumulatedData stringByAppendingString:fragment];
    }

    if (![self parseDoubleQuote])
    {
        return nil;
    }

    return accumulatedData;
}

//
// parseNonEscaped
//
// Attempts to parse a non-escaped field value from the current scan location.
//
// returns the field value or nil.
//
- (NSString *)parseNonEscaped
{
    return [self parseTextData];
}

//
// parseTwoDoubleQuotes
//
// Attempts to parse two double quotes from the current scan location.
//
// returns a string containing two double quotes or nil.
//
- (NSString *)parseTwoDoubleQuotes
{
    if ([scanner scanString:@"\"\"" intoString:NULL])
    {
        return @"\"\"";
    }
    return nil;
}

//
// parseDoubleQuote
//
// Attempts to parse a double quote from the current scan location.
//
// returns @"\"" or nil.
//
- (NSString *)parseDoubleQuote
{
    if ([scanner scanString:@"\"" intoString:NULL])
    {
        return @"\"";
    }
    return nil;
}

//
// parseSeparator
//
// Attempts to parse the separator string from the current scan location.
//
// returns the separator string or nil.
//
- (NSString *)parseSeparator
{
    if ([scanner scanString:separator intoString:NULL])
    {
        return separator;
    }
    return nil;
}

//
// parseLineSeparator
//
// Attempts to parse newline characters from the current scan location.
//
// returns a string containing one or more newline characters or nil.
//
- (NSString *)parseLineSeparator
{
    NSString *matchedNewlines = nil;
    [scanner
        scanCharactersFromSet:[NSCharacterSet newlineCharacterSet]
        intoString:&matchedNewlines];
    return matchedNewlines;
}

//
// parseTextData
//
// Attempts to parse text data from the current scan location.
//
// returns a non-zero length string or nil.
//
- (NSString *)parseTextData
{
    NSString *accumulatedData = [NSString string];
    while (YES)
    {
        NSString *fragment;
        if ([scanner scanUpToCharactersFromSet:endTextCharacterSet intoString:&fragment])
        {
            accumulatedData = [accumulatedData stringByAppendingString:fragment];
        }

        //
        // If the separator is just a single character (common case) then
        // we know we've reached the end of parseable text
        //
        if (separatorIsSingleChar)
        {
            break;
        }

        //
        // Otherwise, we need to consider the case where the first character
        // of the separator is matched but we don't have the full separator.
        //
        NSUInteger location = [scanner scanLocation];
        NSString *firstCharOfSeparator;
        if ([scanner scanString:[separator substringToIndex:1] intoString:&firstCharOfSeparator])
        {
            if ([scanner scanString:[separator substringFromIndex:1] intoString:NULL])
            {
                [scanner setScanLocation:location];
                break;
            }

            //
            // We have the first char of the separator but not the whole
            // separator, so just append the char and continue
            //
            accumulatedData = [accumulatedData stringByAppendingString:firstCharOfSeparator];
            continue;
        }
        else
        {
            break;
        }
    }

    if ([accumulatedData length] > 0)
    {
        return accumulatedData;
    }

    return nil;
}

@end

【讨论】:

  • 感谢您的帮助,虽然我可以使用它,但我想了解我遇到的问题,而不是简单地找到解决方案。下面的用户 Ar Ma 似乎发现了问题所在。我还不能投票,因为我是论坛的新手,但以后会回来投票。
  • 乔丹,出于好奇,我从 Matt Gallagher 那里对 CSVParser 产生了兴趣。是否有针对 ARC 更新的版本?
  • 我在答案中添加了 CSVParser 的 .h 和 .m 以及 ARC
【解决方案2】:

感谢大家的帮助。我在stackoverflow上找到了答案,一个2-3岁的论坛(Adding Core Data to existing iPhone project)...

所以问题似乎是,当我第一次创建项目时,我没有要求使用核心数据,只是后来才这样做。根据我上面发布的帖子:

Add the following to supporting files/projectName-Prefix.pch

#import <CoreData/CoreData.h>

一旦我这样做了,persistenceCoordinator 错误就消失了……

哇,在那里学到了很多教训......

【讨论】:

    【解决方案3】:
    -(void)populateDB {
        NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
        NSManagedObjectContext *context;
        [....]  
    }
    

    问题可能是 [self persistentStoreCoordinator]... 你有一个名为“persistentStoreCoordinator”的函数吗?如果没有,你必须编写函数。

    [编辑]

    - (NSPersistentStoreCoordinator *)persistentStoreCoordinator 
    {
    @synchronized (self)
    {
    if (__persistentStoreCoordinator != nil)
        return __persistentStoreCoordinator;
    
    NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"myProject" ofType:@"sqlite"];
    NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent: @"myProject.sqlite"];
    
    NSError *error;
    if (![[NSFileManager defaultManager] fileExistsAtPath:storePath]) 
    {
        if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
            NSLog(@"Copied starting data to %@", storePath);
        else 
            NSLog(@"Error copying default DB to %@ (%@)", storePath, error);
    }
    
    NSURL *storeURL = [NSURL fileURLWithPath:storePath];
    
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
    
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) 
    {
    
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    
    
    return __persistentStoreCoordinator;
    }    
    }
    

    此代码是否在另一个文件中?也许你忘了导入 .h 文件,其中声明了 persistentStoreCoordinator。

    【讨论】:

    • 这就是我要找的......出于某种原因,我认为persistenceStoreCoordinator是xcode无法解析的内置属性,所以不,我没有这个方法。但是,当我添加它时,Xcode 我遇到了很多问题,开始时 __persistenceStoreCoordinator 没有被声明。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-02
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    • 2011-09-17
    相关资源
    最近更新 更多