【问题标题】:Efficient NSString parsing with NSScanner使用 NSScanner 进行高效的 NSString 解析
【发布时间】:2012-05-01 19:53:09
【问题描述】:

首先,我正在编写一个 iOS 5 应用程序。例如,假设我有以下字符串:

100 - PARK STREET / JAMES PLACE

我想以最有效(和代码优雅)的方式从这个字符串中提取两个道路名称。我已经尝试过使用[string componentsSeparatedByString...] 等的组合,但这变得非常混乱,非常快。此外,它需要大量的条件语句来处理如下情况:

100 - BI-CENTENNIAL DRIVE / JAMES PLACE

因为它包含一个嵌套的连字符,如果我们使用 [string componentsSeparatedByString:@"-"] 并需要重新组装,它将被拆分。

还有一些情况,字符串的格式可能略有不同,比如:

100- BI-CENTENNIAL DRIVE / JAMES PLACE

(数字和连字符之间没有空格)

100-BI-CENTENNIAL DRIVE /JAMES PLACE

(数字周围完全没有空格,斜线和第二个道路名称之间没有空格)

但是,我们总是可以假设在分隔两个道路名称的字符串中只有一个斜杠。

道路名称也应去掉任何前导和尾随空格。

我认为使用NSScanner 可以以更高效、更优雅的方式实现整个过程,但不幸的是,我没有必要的课程经验来使其工作。任何建议将不胜感激。

【问题讨论】:

  • 你读过关于 NSScanner 的文档吗?这里有一些很棒的示例代码:developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…
  • 是的,但我仍然无法理解 NSScanner 的操作方式,尤其是以这种方式拆分字符串。特别是,我真的很想看看比我更了解 NSScanners 的人如何有效地完成这项工作。
  • 使用扫描仪,您不会拆分字符串,而是一次浏览一个字符并做出相应的反应。肯的​​例子是完美的。我建议尝试一下,以了解它是如何工作的。对我来说,这是一个比 REGEX 更简单的解决方案,但它们都可以正常工作。

标签: objective-c ios ios5 foundation nsscanner


【解决方案1】:

这是另一个使用这个可怕的 NSScanner 类的例子。

假设您有一个包含四个值的字符串,并希望将它们转换为 CGRect:

NSString* stringToParse = @"10, 20, 600, 150";             
CGRect rect = [self stringToCGRect:stringToParse];

NSLog(@"Rectangle: %.0f, %.0f, %.0f, %.0f", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);

要做到这一点,你会写一个像这样讨厌的小函数:

-(CGRect)stringToCGRect:(NSString*)stringToParse
{
    NSLog(@"Parsing the string: %@", stringToParse);
    int x, y, wid, hei;

    NSString *subString;
    NSScanner *scanner = [NSScanner scannerWithString:stringToParse];
    [scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];
    [scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&subString];
    x = [subString integerValue];

    [scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];
    [scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&subString];
    y = [subString integerValue];

    [scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];
    [scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&subString];
    wid = [subString integerValue];

    [scanner scanUpToCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:nil];
    [scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&subString];
    hei = [subString integerValue];

    CGRect rect = CGRectMake(x, y, wid, hei);
    return rect;
}

请原谅我的消极情绪,但我很累,现在是晚上 10.30 点,我鄙视不得不编写这样的 Objective-C 代码,我非常清楚使用过去 15 年的任何 Microsoft 开发环境,这都会'只写了一行代码。

咕噜……

【讨论】:

    【解决方案2】:

    您也可以使用Regular Expression

    请注意,在块中我使用捕获块,通过[result rangeAtIndex:i]
    索引 1 现在将是门牌号,索引 2 将返回第一条街道,而 3 将返回第二条街道。

    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[])
    {
    
        @autoreleasepool {
            NSArray *streets = [NSArray arrayWithObjects:@"100 - PARK STREET / JAMES PLACE", @"100 - BI-CENTENNIAL DRIVE / JAMES PLACE", @"100- BI-CENTENNIAL DRIVE / JAMES PLACE", @"100-BI-CENTENNIAL DRIVE /JAMES PLACE", nil];
    
            NSString *text = [streets componentsJoinedByString:@" "];
            NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) {0,1}- {0,1}(\\D+) *\\/ *(\\D+)" options:NSRegularExpressionCaseInsensitive error:nil];
    
            [regex enumerateMatchesInString:text options:0 
                                      range:NSMakeRange(0, [text length]) 
                                 usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) 
            {
                for (int i = 1; i< [result numberOfRanges] ; i++) {
                    NSLog(@"%@", [text substringWithRange:[result rangeAtIndex:i]]);
                }
            }];
        }
        return 0;
    }
    

    输出:

    100
    PARK STREET 
    JAMES PLACE 
    100
    BI-CENTENNIAL DRIVE 
    JAMES PLACE 
    100
    BI-CENTENNIAL DRIVE 
    JAMES PLACE 
    100
    BI-CENTENNIAL DRIVE 
    JAMES PLACE
    

    编辑以响应 cmets

    int main (int argc, const char * argv[])
    {
    
        @autoreleasepool {
            NSArray *streets = [NSArray arrayWithObjects:@"100 - PARK STREET / JAMES PLACE", @"100 - BI-CENTENNIAL DRIVE / JAMES PLACE", @"100- BI-CENTENNIAL DRIVE / JAMES PLACE", @"100-BI-CENTENNIAL DRIVE /JAMES PLACE",@"100 - PARK STREET", nil];
    
            NSRegularExpression *regex1 = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) *- *([^\\/]+) *$" options:NSRegularExpressionCaseInsensitive error:nil];
            NSRegularExpression *regex2 = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) *- *([^\\/]+) *\\/ *([^\\/]+) *$" options:NSRegularExpressionCaseInsensitive error:nil];
            for (NSString *text in streets) {                        
                NSRegularExpression *regex = ([regex1 numberOfMatchesInString:text options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [text length])]) ? regex1 : regex2;
                [regex enumerateMatchesInString:text options:0 
                                          range:NSMakeRange(0, [text length]) 
                                     usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) 
                 {
                     for (int i = 1; i< [result numberOfRanges] ; i++) {
                         NSLog(@"%@", [text substringWithRange:[result rangeAtIndex:i]]);
                     }
    
                 }];
            }
        }
        return 0;
    }
    

    第二次修改

    int main (int argc, const char * argv[])
    {
    
        @autoreleasepool {
            NSArray *streets = [NSArray arrayWithObjects:   @"100 - PARK STREET / JAMES PLACE", 
                                                            @"100 - BI-CENTENNIAL DRIVE / JAMES PLACE", 
                                                            @"100- BI-CENTENNIAL DRIVE / JAMES PLACE", 
                                                            @"100-BI-CENTENNIAL DRIVE /JAMES PLACE",
                                                            @"100 - PARK STREET",
                                                            @"100 - PARK STREET / ",
                                                            @"100 - PARK STREET/ ",
                                                            @"100 - PARK STREET/",
                                nil];
    
            NSRegularExpression *regex1 = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) *- *([^\\/]+) *$" options:NSRegularExpressionCaseInsensitive error:nil];
            NSRegularExpression *regex2 = [NSRegularExpression regularExpressionWithPattern:@"(\\d+) *- *([^\\/]+) *\\/ *([^\\/]*) *$" options:NSRegularExpressionCaseInsensitive error:nil];
            for (NSString *text in streets) { 
    
                text= [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
                NSLog(@"\n>%@<", text);
                NSRegularExpression *regex = ([regex1 numberOfMatchesInString:text options:NSRegularExpressionCaseInsensitive range:NSMakeRange(0, [text length])]) ? regex1 : regex2;
                [regex enumerateMatchesInString:text options:0 
                                          range:NSMakeRange(0, [text length]) 
                                     usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) 
                 {
                     for (int i = 1; i< [result numberOfRanges] ; i++) {
                         NSLog(@"%@", [text substringWithRange:[result rangeAtIndex:i]]);
                     }
    
                 }];
            }
        }
        return 0;
    }
    

    【讨论】:

    • 谢谢,这很完美:) 没有考虑使用正则表达式而不是 NSScanner。
    • 出现了另一种边缘情况。如何修改正则表达式以处理这样的字符串:100 - PARK STREET。换句话说,根本没有斜线——只有一条街道?
    • 我对正则表达式的第一个建议是“考虑一下,如果它真的是这项工作的工具”(参见:regex.info/blog/2006-09-15/247),但你的情况似乎就是这样。跨度>
    • 类似的东西? (\\d+) {0,1}- {0,1}(\\D+)
    • 我猜只有受虐狂不会与正则表达式抗争:)。检查我的新编辑,了解我将如何尝试标准化。第二个正则表达式现在也允许最后一个捕获组为空, * 而不是 +
    【解决方案3】:

    这看起来像是 NSRegularExpression 的工作。

    我认为是 R.E.像

    ^[0-9]+ *- *(.*)$
    

    会匹配你想要的。

    【讨论】:

      【解决方案4】:

      刚刚在我的浏览器中编码:

      NSString* line = @"100- BI-CENTENNIAL DRIVE / JAMES PLACE";
      NSScanner* scanner = [NSScanner scannerWithString:line];
      NSString* number;
      if (![scanner scanUpToString:@"-" intoString:&number])
          /* handle parse failure */;
      NSString* firstRoad;
      if (![scanner scanUpToString:@"/" intoString:&firstRoad])
          /* handle parse failure */;
      NSString* secondRoad = [str substringFromIndex:[scanner scanLocation]];
      

      可能有额外的空格需要从结果字符串中修剪。

      【讨论】:

        猜你喜欢
        • 2015-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-14
        • 2011-07-10
        • 1970-01-01
        • 2011-07-03
        • 1970-01-01
        相关资源
        最近更新 更多