NSPredicate与NSString:findsuperstrings哪个更好/更快?

我有大量的string,我正在查找是否存在给定的子string。 看来有两种合理的方法来做到这一点。

选项1:使用NSString方法rangeOfSubstring并testingrangeOfSubstring是否存在:

 NSRange range = [string rangeOfSubstring:substring]; return (range.location != NSNotFound); 

选项2.使用NSPredicate语法CONTAINS

 NSPredicate *regex = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", substring]; return ([regex evaluateWithObject:string] == YES) 

哪种方法更好,还是有一个很好的选项3,我完全失踪了? 不,我不确定我的意思是“更好”,但是可能在迭代许多string时意味着更快。

您应该使用NSPredicate任何解决scheme的基准和时间,因为根据我的经验, NSPredicate可能会非常缓慢。

为了简单起见,我将使用一个简单for(NSString *string in stringsArray) { }types的循环。 循环体将包含一个简单的rangeOfSubstring检查。 通过使用CFStringFind() ,你可能会提高几个百分点的性能,但是如果你正在search大量的string,你只会看到一个好处。 使用CFStringFind()的好处是可以避免非常小的Objective-C消息分配开销。 同样,当你search“很多”string(对于一些总是改变“很多”的值)的情况下,通常只是一个胜利,你应该总是基准确定。 如果可以的话, rangeOfString:使用更简单的Objective-C rangeOfString:方法。

更复杂的方法是使用NSEnumerationConcurrent选项的^ Blocksfunction。 NSEnumerationConcurrent只是一个暗示,如果可能的话,你希望枚举同时发生,如果它不支持并发枚举,一个实现可以自由地忽略这个提示。 但是,你的标准NSArray很可能要实现并发枚举。 在实践中,这具有将NSArray中的所有对象分开并将其分割到可用的CPU中的效果。 您需要小心如何修改^ Block在多个线程中访问的状态和对象。 这是一个可能的方法:

 // Be sure to #include <libkern/OSAtomic.h> __block volatile OSSpinLock spinLock = OS_SPINLOCK_INIT; __block NSMutableArray *matchesArray = [NSMutableArray array]; [stringsToSearchArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSRange matchedRange = [obj rangeOfString:@"this"]; if(matchedRange.location != NSNotFound) { OSSpinLockLock((volatile OSSpinLock * volatile)&spinLock); [matchesArray addObject:obj]; OSSpinLockUnlock((volatile OSSpinLock * volatile)&spinLock); } }]; // At this point, matchesArray will contain all the strings that had a match. 

这使用一个轻量级的OSSpinLock来确保一次只有一个线程可以访问和更新matchesArray 。 你也可以在上面使用相同的CFStringFind()build议。

另外,你应该知道rangeOfString:本身不会匹配“单词边界”。 在上面的例子中,我使用了这个单词this ,它可以匹配stringA paleolithist walked in to the bar...即使它不包含这个词。

这个小皱纹的最简单的解决scheme是使用ICU正则expression式,并利用它的“增强的分词”function。 要做到这一点,你有几个select:

  • NSRegularExpression ,目前只适用于> 4.2或> 4.3的iOS(我忘记了哪个)。
  • RegexKit Lite ,通过RegexKitLite-4.0.tar.bz2
  • NSPredicate ,通过SELF MATCHES '(?w)\b...\b' 。 这样做的好处是它不需要任何额外的(即RegexKit Lite ),并且可以在所有(?)版本的Mac OS X和iOS> 3.0上使用。

以下代码显示如何通过NSPredicate在ICU正则expression式中使用增强的分词function:

 NSString *searchForString = @"this"; NSString *regexString = [NSString stringWithFormat:@".*(?w:\\b\\Q%@\\E\\b).*", searchForString]; NSPredicate *wordBoundaryRegexPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regexString]; NSArray *matchesArray = [stringsToSearchArray filteredArrayUsingPredicate:wordBoundaryRegexPredicate]; 

你可以通过用(?wi: regexStringreplace(?w: in regexString来使search不区分大小写。

正则expression式,如果你感兴趣,基本上说

  • .*(?w:...).*表示“匹配(?w:...)部分之后的任何内容”(即,我们只对(?w:...)部分感兴趣)。
  • (?w:...)表示“在圆括号内打开ICU增强的分词/查找function”。
  • \\b...\\b (它实际上只有一个反斜杠,当它在@""string中)时,任何反斜杠都必须被反斜杠表示“匹配字边界”。
  • \\Q...\\E表示“将文本紧接在\Q ,直到\E作为文本文本处理(认为”Quote“和”End“)。 换句话说,“引用文本文本”中的任何字符都没有它们特殊的正则expression式含义。

\Q...\E的原因是您可能希望匹配searchForString中的文字字符。 没有这个, searchForString将被视为正则expression式的一部分。 作为一个例子,如果searchForStringthis? ,那么没有\Q...\E不会匹配这个文字stringthis? ,但不pipe是this还是this ,这可能不是你想要的。 🙂

大小写(n):如果您有string数组来testing子string,最好使用NSPredicate

 NSPredicate *regex = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", substring]; NSArray *resultArray = [originalArrayOfStrings filteredArrayUsingPredicate:regex]; 

这将返回包含子string的string数组。

如果使用NSRange ,在这种情况下,您需要手动循环访问数组的所有string对象,显然它会比NSPredicate慢。