Find all locations of substring in NSString (not just first) Find all locations of substring in NSString (not just first) xcode xcode

Find all locations of substring in NSString (not just first)


You can use rangeOfString:options:range: and set the third argument to be beyond the range of the first occurrence. For example, you can do something like this:

NSRange searchRange = NSMakeRange(0,string.length);NSRange foundRange;while (searchRange.location < string.length) {    searchRange.length = string.length-searchRange.location;    foundRange = [string rangeOfString:substring options:0 range:searchRange];    if (foundRange.location != NSNotFound) {        // found an occurrence of the substring! do stuff here        searchRange.location = foundRange.location+foundRange.length;    } else {        // no more substring to find        break;    }}


Swift 3.0

Find all locations of substring i

let text = "This is the text and i want to replace something"let mutableAttributedString = NSMutableAttributedString(string: text)var searchRange = NSRange(location: 0, length: text.characters.count)var foundRange = NSRange()while searchRange.location < text.characters.count {    searchRange.length = text.characters.count - searchRange.location    foundRange = (text as NSString).range(of: "i", options: NSString.CompareOptions.caseInsensitive, range: searchRange)    if foundRange.location != NSNotFound {        // found an occurrence of the substring! do stuff here        searchRange.location = foundRange.location + foundRange.length        mutableAttributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: foundRange)    }    else {        // no more substring to find        break    }}//ApplytextLabel.attributedText = mutableAttributedString;

And this output-

enter image description here


This is my solution. Basically, the algorithm traverses the string looking for substring matches and returns those matches in an array.

Since an NSRange is a struct it cannot be added to the array directly. By using NSValue, I can encode the match first and then add it to the array. To retrieve the range, I then decode the NSValue object to an NSRange.

#import <Foundation/Foundation.h>NSRange makeRangeFromIndex(NSUInteger index, NSUInteger length) {    return NSMakeRange(index, length - index);}NSArray<NSValue *> * allLocationsOfStringMatchingSubstring(NSString *text, NSString *pattern) {    NSMutableArray *matchingRanges = [NSMutableArray new];    NSUInteger textLength = text.length;    NSRange match = makeRangeFromIndex(0, textLength);    while(match.location != NSNotFound) {        match = [text rangeOfString:pattern options:0L range:match];        if (match.location != NSNotFound) {            NSValue *value = [NSValue value:&match withObjCType:@encode(NSRange)];            [matchingRanges addObject:value];            match = makeRangeFromIndex(match.location + 1, textLength);        }    }    return [matchingRanges copy];}int main(int argc, const char * argv[]) {    @autoreleasepool {        NSString *text = @"TATACCATGGGCCATCATCATCATCATCATCATCATCATCATCACAG";        NSString *pattern = @"CAT";        NSArray<NSValue *> *matches = allLocationsOfStringMatchingSubstring(text, pattern);        NSLog(@"Text: %@", text);        NSLog(@"Pattern: %@", pattern);        NSLog(@"Number of matches found: %li", matches.count);        [matches enumerateObjectsUsingBlock:^(NSValue *obj, NSUInteger idx, BOOL *stop) {            NSRange match;            [obj getValue:&match];            NSLog(@"   Match found at index: %li", match.location);        }];    }    return 0;}