如何为iphone解析文件?我应该使用NSScanner吗?

时间:2009-12-29 02:13:44

标签: iphone cocoa csv parsing nsstring

所以我是iphone开发的新手,但我正在尝试学习如何获取CSV文件并将其读取并使用Core Data保存(我认为这是最好的方法吗?)以便我可以将其显示在iphone的桌面视图。下面是我正在使用的csv文件类型的示例;

13,1,1,历史,,,,,, 1个
,263,1,史密斯,鲍勃,大一
,317,2,琼斯,约翰,Sophmore
14,2,1,数学,,,,,, 1个
,311,1,金,玛丽,大一
,352,2,Doe,Fred,Senior

如果第一个数字是类的类型(即13 =历史),第二个数字是多少个部分(即1 = 1个等级),第三个数字是会议模式(即1 =星期一,星期三,星期五) ,然后是班级的名称。我不太关心课程的其余部分,所以我想我可以让它忽略那些角色。

对于第2行和第3行,逗号后面的第一个数字是学号,然后是座位号,然后是姓氏,名字,学年。

所以我认为有两个主要挑战。首先是如何解析数据,以便我可以知道每个类中的许多人和谁,所以我可以将其调用到表视图中(稍后添加),然后我不知道如何将整数值与会议模式或班级名称(即13 =历史,1 =周一/周三/周五)

非常感谢你的帮助

7 个答案:

答案 0 :(得分:14)

答案 1 :(得分:5)

此处可以使用NSString方法componentsSeparatedByString:,但特定于CSV的库可能更容易使用。这里有一篇关于解析CSV数据的好文章(含代码):

答案 2 :(得分:4)

对于解析,我会使用componentsSeparatedByString:NSString类的方法。这类似于perl或ruby中的split函数

答案 3 :(得分:3)

我有一个用于Objective-C的CSV解析器,它会解析你抛出的任何CSV文件(如果它失败了,请告诉我,以便我可以解决它。)

https://github.com/davedelong/CHCSVParser

答案 4 :(得分:0)

您可以使用RegexKitLite。关于如何执行此操作的documentation has an example只有17行,并且包含注释。虽然您的milage可能会有所不同,但我一般认为它是解析CSV数据的最快方法之一,也是最容易修改以满足您需求的方法之一,因为它只需要几行来完成整个事情。< / p>

答案 5 :(得分:0)

你可能想看看Matt Gallagher关于解析CSV的“Cocoa With Love”一文:http://cocoawithlove.com/2009/11/writing-parser-using-nsscanner-csv.html。他写了一个完整的语法和几个有用的课程,你可以直接进入你的项目 霍华德

答案 6 :(得分:0)

@JohnnySoftware指出的文章不再有效,我在wayback machine中找到它,并且正在复制下面的内容:

在很多情况下,MacResearch的读者已经发布了一些问题,询问您如何解析Cocoa中的CSV(逗号分隔值)数据。 CSV是一种用于表示表格的简单标准;它用于各种各样的领域,从科学到金融 - 基本上任何表都需要存储在文本文件中。

我最近在我的闪存卡应用程序Mental Case中添加了CSV导入。在我开始之前,我认为在Google上搜索一些Objective-C示例代码或开源库是一件小事。我在Python等脚本语言中找到了解决方案,但没有基于Cocoa的解决方案。经过一两个小时的搜索,我意识到如果我想要一个可可原生的解决方案,我将不得不自己动手。在这个简短的教程中,我将向您展示我的想法,希望能为您省去自己做的麻烦。 简单的CSV

如果您事先知道数据的结构,并且您不必处理引用的字符串,那么解析CSV实际上可以非常简单。事实上,我在早期的教程中解决了这个问题,该教程以CSV格式存储光谱。

- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName 
    error:(NSError **)outError 
{
    NSString *fileString = [NSString stringWithContentsOfURL:absoluteURL 
        encoding:NSUTF8StringEncoding error:outError];
    if ( nil == fileString ) return NO;
    NSScanner *scanner = [NSScanner scannerWithString:fileString];
    [scanner setCharactersToBeSkipped:
        [NSCharacterSet characterSetWithCharactersInString:@"\n, "]];
    NSMutableArray *newPoints = [NSMutableArray array];
    float energy, intensity;
    while ( [scanner scanFloat:&energy] && [scanner scanFloat:&intensity] ) {
        [newPoints addObject:
            [NSMutableDictionary dictionaryWithObjectsAndKeys:
                [NSNumber numberWithFloat:energy], @"energy",
                [NSNumber numberWithFloat:intensity], @"intensity",
                nil]];
    }
    [self setPoints:newPoints];
    return YES;
}

NSScanner类是你用来在Cocoa中进行大部分字符串解析的方法。在上面的示例中,假设CSV文件是特定形式,即它有两列,每列包含一个十进制数。通过告诉扫描仪跳过逗号 [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"\n, "]]; 每行的解析减少到一行     while ( [scanner scanFloat:&energy] && [scanner scanFloat:&intensity] ) { scanFloat:方法将尝试读取浮点数,失败时返回NO。因此while循环将继续,直到格式不符合预期。

常规CSV

正如您所看到的,解析CSV数据非常简单,但情况并非总是如此。当你必须处理一般的CSV数据时,事情会变得非常复杂,因为你必须考虑字符串包含引号的可能性,甚至可以扩展到多行。例如,以下是有效的CSV数据行,包含两列:

  

&#34;快速的棕色狐狸&#34;,&#34;跳过&#34;&#34;懒惰&#34;&#34;,狗&#34;

如果你没有弄明白,双引号在字符串中被视为单引号,给出了两个字符串&#39;快速的棕色狐狸&#39;并且&#39;跳过&#34;懒惰&#34;狗&#39;。

解析这种通用形式的CSV比简单形式困难得多,我花了很长时间才想出一些干净的代码来完成它。但我认为我最终成功了。这是:(更新:我已更改此代码以正确处理所有换行品种。)

@implementation NSString (ParsingExtensions)

-(NSArray *)csvRows {
    NSMutableArray *rows = [NSMutableArray array];

    // Get newline character set
    NSMutableCharacterSet *newlineCharacterSet = (id)[NSMutableCharacterSet whitespaceAndNewlineCharacterSet];
    [newlineCharacterSet formIntersectionWithCharacterSet:[[NSCharacterSet whitespaceCharacterSet] invertedSet]];

    // Characters that are important to the parser
    NSMutableCharacterSet *importantCharactersSet = (id)[NSMutableCharacterSet characterSetWithCharactersInString:@",\""];
    [importantCharactersSet formUnionWithCharacterSet:newlineCharacterSet];

    // Create scanner, and scan string
    NSScanner *scanner = [NSScanner scannerWithString:self];
    [scanner setCharactersToBeSkipped:nil];
    while ( ![scanner isAtEnd] ) {        
        BOOL insideQuotes = NO;
        BOOL finishedRow = NO;
        NSMutableArray *columns = [NSMutableArray arrayWithCapacity:10];
        NSMutableString *currentColumn = [NSMutableString string];
        while ( !finishedRow ) {
            NSString *tempString;
            if ( [scanner scanUpToCharactersFromSet:importantCharactersSet intoString:&tempString] ) {
                [currentColumn appendString:tempString];
            }

            if ( [scanner isAtEnd] ) {
                if ( ![currentColumn isEqualToString:@""] ) [columns addObject:currentColumn];
                finishedRow = YES;
            }
            else if ( [scanner scanCharactersFromSet:newlineCharacterSet intoString:&tempString] ) {
                if ( insideQuotes ) {
                    // Add line break to column text
                    [currentColumn appendString:tempString];
                }
                else {
                    // End of row
                    if ( ![currentColumn isEqualToString:@""] ) [columns addObject:currentColumn];
                    finishedRow = YES;
                }
            }
            else if ( [scanner scanString:@"\"" intoString:NULL] ) {
                if ( insideQuotes && [scanner scanString:@"\"" intoString:NULL] ) {
                    // Replace double quotes with a single quote in the column string.
                    [currentColumn appendString:@"\""]; 
                }
                else {
                    // Start or end of a quoted string.
                    insideQuotes = !insideQuotes;
                }
            }
            else if ( [scanner scanString:@"," intoString:NULL] ) {  
                if ( insideQuotes ) {
                    [currentColumn appendString:@","];
                }
                else {
                    // This is a column separating comma
                    [columns addObject:currentColumn];
                    currentColumn = [NSMutableString string];
                    [scanner scanCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:NULL];
                }
            }
        }
        if ( [columns count] > 0 ) [rows addObject:columns];
    }

    return rows;
}
@end

(我将此代码发布到公共领域,因此请随意使用。) 此代码旨在成为NSString的一个类别。我们的想法是,它会将字符串解析为行和列,假设它是CSV格式。结果是一个数组数组;包含数组中的条目表示行,而包含的数组中的条目表示每行中的列。

代码本身相当简单:它由一个大的while循环组成,它一直持续到整个字符串被解析为止。内部while循环查看每行CSV数据,查找重要的地标,如行尾,开头或右数引号或逗号。通过跟踪开始和结束引号,它能够正确处理嵌入在引用字符串中的逗号和换行符。

<强>结论

NSScanner是一个用于解析字符串的有用类。它可能不像Perl和Python等脚本语言中的正则表达式那么强大,但只有几个方法 - 例如,scanString:intoString:scanUpToCharactersFromSet:intoString:,scanFloat: - 你可以实现很多。如果你需要在你的一个Cocoa项目中进行任何基本的字符串解析,请看一下。