在ObjC中获取两个字符串之间的子字符串

16

我正在尝试解析一个字符串并获取其中的另一个字符串。

例如:

Hello world this is a string

我需要找到 "world" 和 "is"(this)之间的字符串。我已经搜索了一些资料,但由于我对Objective C还很陌生,所以还没有找到如何实现这个功能。有没有人知道如何使用正则表达式或其他方法实现?

4个回答

33

Jacques提供的正则表达式解决方案是有效的,需要iOS 4.0及更高版本。使用正则表达式也相当慢,并且在搜索表达式是已知字符串常量时会过度使用。

你可以使用NSString上的方法或名为NSScanner的类来解决该问题。它们自iPhone OS 2.0以来一直可用,而且在此之前很久就已经存在了,实际上是在Mac OS X 10.0之前就有了 :)

所以你想要一个像这样的NSString新方法吗?

@interface NSString (CWAddition)
- (NSString*) stringBetweenString:(NSString*)start andString:(NSString*)end;
@end
没有问题,我们假设如果找不到这样的字符串,应该返回nil。 使用NSString的实现非常直接:
@implementation NSString (NSAddition)
- (NSString*) stringBetweenString:(NSString*)start andString:(NSString*)end {
    NSRange startRange = [self rangeOfString:start];
    if (startRange.location != NSNotFound) {
        NSRange targetRange;
        targetRange.location = startRange.location + startRange.length;
        targetRange.length = [self length] - targetRange.location;   
        NSRange endRange = [self rangeOfString:end options:0 range:targetRange];
        if (endRange.location != NSNotFound) {
           targetRange.length = endRange.location - targetRange.location;
           return [self substringWithRange:targetRange];
        }
    }
    return nil;
}
@end

你可以使用NSScanner类进行实现:

@implementation NSString (NSAddition)
- (NSString*) stringBetweenString:(NSString*)start andString:(NSString*)end {
    NSScanner* scanner = [NSScanner scannerWithString:self];
    [scanner setCharactersToBeSkipped:nil];
    [scanner scanUpToString:start intoString:NULL];
    if ([scanner scanString:start intoString:NULL]) {
        NSString* result = nil;
        if ([scanner scanUpToString:end intoString:&result]) {
            return result;
        }
    }
    return nil;
}
@end

请注意,此解决方案不能确保world和is在单词边界上。您可以添加更多代码来处理这个问题,但这可能会很棘手,因此我建议只使用正则表达式(无论是NSRegularExpression还是iOS 3的RegexKitLite)使其更容易处理所有边角情况。进行一些分析以查看是否需要手动编写解决方案。 - Jacques
谢谢!我还需要找到出现多次的字符串...例如:<tag>Hello</tag><tag>Bye</tag>我想我可以添加另一个参数(出现次数),然后循环 X 次开始的字符串,对吧?我应该能够解决它,特别是你给了我 NSRange 的起点等。再次感谢! - Andrew M
没问题,只需要使用上一次搜索的范围来限制下一次搜索的范围即可。 - PeyloW
很好的答案,但在NSString扩展解决方案中,您应该像这样保护不找到结束字符串,就在NSRange endRange = [self rangeOfString:end options:0 range:targetRange];语句之前,否则它会崩溃:NSRange checkEndRange = [self rangeOfString:end]; 如果(checkEndRange.location!= NSNotFound) - Richie Hyatt

12

这是对 PeyloW 回答的简单修改,它返回在起始字符串和结束字符串之间的所有字符串:

-(NSMutableArray*)stringsBetweenString:(NSString*)start andString:(NSString*)end
{

  NSMutableArray* strings = [NSMutableArray arrayWithCapacity:0];

  NSRange startRange = [self rangeOfString:start];

  for( ;; )
  {

    if (startRange.location != NSNotFound)
    {

      NSRange targetRange;

      targetRange.location = startRange.location + startRange.length;
      targetRange.length = [self length] - targetRange.location;   

      NSRange endRange = [self rangeOfString:end options:0 range:targetRange];

      if (endRange.location != NSNotFound)
      {

        targetRange.length = endRange.location - targetRange.location;
        [strings addObject:[self substringWithRange:targetRange]];

        NSRange restOfString;

        restOfString.location = endRange.location + endRange.length;
        restOfString.length = [self length] - restOfString.location;

        startRange = [self rangeOfString:start options:0 range:restOfString];

      }
      else
      {
        break;
      }

    }
    else
    {
      break;
    }

  }

  return strings;

}

2
对于其他人.. 添加一个名为'originalString'的参数,并将'self'替换为它。 - iRoid Solutions

3

请参阅ICU正则表达式用户指南

如果您知道只会有一个结果:

NSRegularExpression *regex = [NSRegularExpression
    regularExpressionWithPattern:@"\bworld\s+(.+)\s+is\b" options:0 error:NULL]

NSTextCheckingResult *result = [regex firstMatchInString:string
    options:0 range:NSMakeRange(0, [string length]];

// Gets the string inside the first set of parentheses in the regex
NSString *inside = [string substringWithRange:[result rangeAtIndex:1]];

\b 会确保在 world 前后有单词边界(所以 "hello world this isn't a string" 不会匹配)。\s 会吞掉 world 后和 is 前的任何空格。.+? 找到你要查找的内容,? 使它非贪婪,这样 "hello world this is a string hello world this is a string" 就不会得到 "this a string hello world this"。 我让你自己想出如何处理多个匹配项。NSRegularExpression 文档应该能帮助你。
如果您想确保匹配不跨越句子边界,可以使用 ([^.]+?) 而不是 (.+?),或者您可以在 options 中使用 NSStringEnumerationBySentences,对字符串使用 enumerateSubstringsInRange:options:usingBlock:。
所有这些都需要4.0+。如果你想支持3.0+,请看一下 RegexKitLite。

谢谢你的帮助!我需要支持OS3/iPad,所以目前这不是一个选项。一旦iPad的OS 4推出,我可以切换到使用Regex的实现。 - Andrew M

1
如果它只是由空格分隔的字符串,您可以使用以下代码:要么
[string componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]

或者

NSMutableArray *parts = [NSMutableArray arrayWithCapacity:1];

NSScanner *scanner = [NSScanner scannerWithString:string];
NSString *token;
while ([scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet]] intoString:&token]) {
    [parts addObject:token];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接