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

后端 未结 6 1814
广开言路
广开言路 2020-11-30 04:12

There is a substring that occurs in a string several times. I use rangeOfString, but it seems that it can only find the first location. How can I find all the l

相关标签:
6条回答
  • 2020-11-30 04:28

    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;
    }
    
    0 讨论(0)
  • 2020-11-30 04:28

    Here is a version in Swift 2.2 of PengOne's answer with input from kevinlawler and Gibtang

    Note: string and substring are of type NSString

    let fullStringLength = (string as String).characters.count
    var searchRange = NSMakeRange(0, fullStringLength)
    while searchRange.location < fullStringLength {
        searchRange.length = fullStringLength - searchRange.location
        let foundRange = string.rangeOfString(substring as String, options: .CaseInsensitiveSearch, range: searchRange)
        if foundRange.location != NSNotFound {
            // found an occurrence of the substring! do stuff here
            searchRange.location = foundRange.location + 1
        } else {
            // no more strings to find
            break
        }
    }
    
    0 讨论(0)
  • 2020-11-30 04:30

    I suggest using regular expression because it's a more declarative way and has fewer lines of code to write.

    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"%@" options:nil error:nil];
    NSString *toSearchStr = @"12312 %@ Text %@ asdsa %@";
    __block int occurs = 0;
    [regex enumerateMatchesInString:toSearchStr options:0 range:NSMakeRange(0, toSearchStr.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
           occurs++;
    }];
    // occurs == 3
    
    0 讨论(0)
  • 2020-11-30 04:32

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-30 04:43

    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
        }
    }
    
    //Apply
    textLabel.attributedText = mutableAttributedString;
    

    And this output-

    0 讨论(0)
  • 2020-11-30 04:43

    Passing nil to [string rangeOfString:substring options:nil range:searchRange]; shows a warning.

    To get rid of the warning, put in an enum from this group

    enum {
       NSCaseInsensitiveSearch = 1,
       NSLiteralSearch = 2,
       NSBackwardsSearch = 4,
       NSAnchoredSearch = 8,
       NSNumericSearch = 64,
       NSDiacriticInsensitiveSearch = 128,
       NSWidthInsensitiveSearch = 256,
       NSForcedOrderingSearch = 512,
       NSRegularExpressionSearch = 1024
    };
    

    https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html#//apple_ref/doc/constant_group/Search_and_Comparison_Options

    0 讨论(0)
提交回复
热议问题