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:
regexString
replace(?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式的一部分。 作为一个例子,如果searchForString
是this?
,那么没有\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
慢。