使用NSScanner通过逗号将字符串分隔为NSArray

时间:2014-04-18 00:50:10

标签: objective-c csv nsstring nsarray nsscanner

当逗号嵌入到引号中时,是否有人知道如何使用NSScanner将字符串用逗号分隔成数组?

在我使用之前:

  NSArray *arrData = [strData componentsSeparatedByString:@","];

但是我在字符串里面有引号,里面有逗号,我希望这些不要分开。我之前从未与NSScanner合作过,而且我正在努力与文档达成协议。有没有人以前做过类似的事情?

2 个答案:

答案 0 :(得分:1)

如果你必须使用NSScanner,你可以做这样的事情。

        NSScanner *scanner = [[NSScanner alloc] initWithString:@"\"Foo, Inc\",0.00,1.00,\"+1.5%\",\"+0.2%\",\"Foo"];
        NSCharacterSet *characters = [NSCharacterSet characterSetWithCharactersInString:@"\","];
        [scanner setCharactersToBeSkipped:nil];
        NSMutableArray *words = [[NSMutableArray alloc]init];
        NSMutableString *word = [[NSMutableString alloc] init];
        BOOL inQuotes = NO;
        while(scanner.isAtEnd == NO)
        {
            NSString *subString;
            [scanner scanUpToCharactersFromSet:characters intoString:&subString];
            NSUInteger currentLocation = [scanner scanLocation];
            if(currentLocation >= scanner.string.length)
            {
                if(subString.length > 0)
                    [words addObject:subString];
                break;
            }
            if([scanner.string characterAtIndex:currentLocation] == '"')
            {
                inQuotes = !inQuotes;
                if(subString == nil)
                {
                    [scanner setScanLocation:currentLocation + 1];
                    continue;
                }
                [word appendFormat:@"%@",subString];
                if(word.length > 0)
                   [words addObject:word.copy];
                [word deleteCharactersInRange:NSMakeRange(0, word.length)];
            }
            if([scanner.string characterAtIndex:currentLocation] == ',')
            {
                if(subString == nil)
                {
                    [scanner setScanLocation:currentLocation + 1];
                    continue;
                }
                if(inQuotes == NO)
                    [words addObject:subString];
                else
                    [word appendFormat:@"%@,",subString];
            }
            [scanner setScanLocation:currentLocation + 1];
        }

修改 这给出了以下输出:

  

Foo,Inc

     

0.00

     

1.00

     

1.5%

     

+ 0.2%

     

希望这是你想要的

正如您所看到的那样复杂且非常容易出错,我建议您使用正则表达式。

答案 1 :(得分:1)

这与lead_the_zeppelin's answer的一般程序相同,但我认为它更直截了当。我们使用NSMutableStringaccum来构建每个评论内部分,其中可能包含任意数量的引用部分。

每次迭代,我们扫描到逗号,引号或字符串的结尾,以先到者为准。如果我们找到一个逗号,那么到目前为止积累的任何内容都应该保存。对于引用,我们选取​​所有内容直至结束引号 -​​ 这是避免将引号内的逗号解释为分割点的关键。请注意,如果报价并非总是平衡,则此不会起作用。如果它们都不是,我们已经到达字符串的末尾,所以我们保存累积的字符串并退出。

// Demo data
NSArray * commaStrings = @[@"This, here, \"has\" no quoted, commas.",
                           @"This \"has, no\" unquoted \"commas,\"",
                           @"This, has,no,quotes",
                           @"This has no commas",
                           @"This has, \"a quoted\" \"phrase, followed\", by a, quoted phrase",
                           @"\"This\", one, \"went,\", to, \"mar,ket\"",
                           @"This has neither commas nor quotes",
                           @"This ends with a comma,"];

NSCharacterSet * commaQuoteSet = [NSCharacterSet characterSetWithCharactersInString:@",\""];

for( NSString * commaString in commaStrings ){

    NSScanner * scanner = [NSScanner scannerWithString:commaString];
    // Scanner ignores whitespace by default; turn that off.
    [scanner setCharactersToBeSkipped:nil];

    NSMutableArray * splitStrings = [NSMutableArray new];

    NSMutableString * accum = [NSMutableString new];

    while( YES ){

        // Set to an empty string for the case where the scanner is
        // at the end of the string and won't scan anything;
        // appendString: will die if its argument is nil.
        NSString * currScan = @"";
        // Scan up to a comma or a quote; this will go all the way to
        // the end of the string if neither of those exists.
        [scanner scanUpToCharactersFromSet:commaQuoteSet
                                intoString:&currScan];
        // Add the just-scanned material to whatever we've already got.
        [accum appendString:currScan];

        if( [scanner scanString:@"," intoString:NULL] ){
            // If it's a comma, save the accumulated string,
            [splitStrings addObject:accum];
            // clear it out,
            accum = [NSMutableString new];
            // and keep scanning.
            continue;
        }
        else if( [scanner scanString:@"\"" intoString:NULL] ) {
            // If a quote, append the quoted segment to the accumulation,
            [scanner scanUpToString:@"\""
                         intoString:&currScan];
            [accum appendFormat:@"\"%@\"", currScan];
            // and continue, appending until the next comma.
            [scanner scanString:@"\"" intoString:NULL];
            continue;
        }
        else {
            //Otherwise, there's nothing else to split; 
            // just save the remainder of the string
            [splitStrings addObject:accum];
            break;
        }

    }
    NSLog(@"%@", splitStrings);
}

另外,as Chuck suggested,您可能只想获得一个CSV解析器。