我有以下代码来搜索NSString
:
for (NSDictionary *obj in data) {
NSString *objQuestion = [obj objectForKey:@"Question"];
NSRange dataRange = [objQuestion rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (dataRange.location != NSNotFound) {
[filteredData addObject:obj];
}
}
这很好,但有一个问题。如果objQuestion
是:"绿-黄-红"并且我搜索"黄-绿-红",则对象将不会显示,因为我的搜索顺序不正确。
我该如何更改代码,以便无论我以何种顺序搜索单词,对象都会显示?
您应该将搜索文本分解为多个单词并搜索每个单词。
NSArray *wordArray= [searchText componentsSeparatedByString: @" "];
for (NSDictionary *obj in data) {
NSString *objQuestion = [obj objectForKey:@"Question"];
BOOL present = NO;
for (NSString *s in wordArray) {
if (s) {
NSRange dataRange = [objQuestion rangeOfString:s options:NSCaseInsensitiveSearch];
if (dataRange.location != NSNotFound) {
present = YES;
}
}
}
if (present) {
[filteredData addObject:obj];
}
}
所以你想基本上进行关键字搜索吗?我建议进行正则表达式搜索。其中单词可以按任何顺序排列。
像这样的东西。
(your|test|data)? *(your|test|data)? *(your|test|data)?
可以在NSRegularExpressoin 中使用
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(your|test|data)? *(your|test|data)? *(your|test|data)?" options:NSRegularExpressionCaseInsensitive error:&error];
int numMatches = [regex numberOfMatchesInString:searchString options:0 range:NSMakeRange(0, [searchString length])];];
这将以有效的方式匹配任何订单。
不确定regex是否适用于Obj C,因为我现在没有mac,但它应该可以。
您可能需要考虑搜索输入字符串并不总是像您期望的那样干净,并且可能包含标点符号、括号等。
你也会想放松口音。
我喜欢使用正则表达式来解决这类问题,因为您正在寻找一种允许对搜索词进行任意排序的解决方案,所以我们需要重新处理搜索字符串。我们也可以使用正则表达式来实现这一点,所以模式是通过正则表达式替换来构建的,这只是出于原则。您可能需要将其彻底记录下来。
因此,这里有一个代码片段可以做这些事情:
// Use the Posix locale as the lowest common denominator of locales to
// remove accents.
NSLocale *enLoc = [[NSLocale alloc] initWithLocaleIdentifier: @"en_US_POSIX"];
// Mixed bag of genres, but for testing purposes we get all the accents we need
NSString *orgString = @"Beyoncé Motörhead Händel";
// Clean string by removing accents and upper case letters in Posix encoding
NSString *string = [orgString stringByFoldingWithOptions: NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch
locale: enLoc ];
// What the user has typed in, with misplaced umlaut and all
NSString *orgSearchString = @"handel, mötorhead, beyonce";
// Clean the search string, too
NSString *searchString = [orgSearchString stringByFoldingWithOptions: NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch
locale: enLoc ];
// Turn the search string into a regex pattern.
// Create a pattern that looks like: "(?=.*handel)(?=.*motorhead)(?=.*beyonce)"
// This pattern uses positive lookahead to create an AND logic that will
// accept arbitrary ordering of the words in the pattern.
// The b expression matches a word boundary, so gets rid of punctuation, etc.
// We use a regex to create the regex pattern.
NSString *regexifyPattern = @"(?w)(\W*)(\b.+?\b)(\W*)";
NSString *pattern = [searchString stringByReplacingOccurrencesOfString: regexifyPattern
withString: @"(?=.*$2)"
options: NSRegularExpressionSearch
range: NSMakeRange(0, searchString.length) ];
NSError *error;
NSRegularExpression *anyOrderRegEx = [NSRegularExpression regularExpressionWithPattern: pattern
options: 0
error: &error];
if ( !anyOrderRegEx ) {
// Regex patterns are tricky, programmatically constructed ones even more.
// So we check if it went well and do something intelligent if it didn't
// ...
}
// Match the constructed pattern with the string
NSUInteger numberOfMatches = [anyOrderRegEx numberOfMatchesInString: string
options: 0
range: NSMakeRange(0, string.length)];
BOOL found = (numberOfMatches > 0);
Posix区域设置标识符的使用在Apple的技术说明中进行了讨论。
理论上,如果用户为正则表达式输入具有特殊含义的字符,则会出现边缘情况,但由于第一个正则表达式删除了非单词字符,因此应该以这种方式解决。有点未经计划的积极副作用,因此值得验证。
如果您对基于正则表达式的解决方案不感兴趣,那么代码折叠对于基于"正常"NSString的搜索可能仍然有用。