Search code examples
iosobjective-cnsstringsubstringnsrange

Finding position of a character related to the position of another character in NSString


say I have a txt file with data as follows;

12345,123,98765,JOHN,RL,s/w
12345,123,98765,SAM,RL,s/w 
12345,123,98765,MIC,RL,s/w
12345,123,98765,MIC,RL,s/w
12345,123,98765,MIC,RL,s/w
12345,123,98765,MIC,RL,s/w
12345,123,98765,MIC,RL,s/w
12345,123,98765,AMI,RL,s/w
12345,123,98765,THIMOTHY,RL,s/w

It is bundled within my app and I am reading the data to a nsstring. And I am passing a search term, say 'MIC'. When I pass this search term, I want to get the lines which having the search term and delete all other lines from the string.

I have tried following code:

NSString *searchTerm = @"MIC";
    NSRange rangess = [dataToBeParsed rangeOfString:searchTerm];

    NSRange range = [dataToBeParsed rangeOfString:@"" options:NSBackwardsSearch range:rangess];
    NSLog(@"range.location: %lu", range.location);
    NSString *substring = [dataToBeParsed substringFromIndex:range.location+1];
    NSLog(@"substring: \n%@'", substring);

I am able to find the location of search term and clear the string content till search location. But I want to get the complete line.

Note: In my original doc, the lines which having a search term will be listed continuously as in the doc.

Please help.


Solution

  • I assume that two things cause the greatest costs:

    • searching the whole string again and again

    • creation of instances for partial string.

    However, you can get rid of both by using ranges on the whole string instead of creating substrings:

    NSUInteger length = [source length];
    NSRange searchRange = NSMakeRange( 0, length );
    NSRange hitRange = NSMakeRange( 0, 0 );
    
    // Iterate over the string unless you do not find any hit
    while( (hitRange = [source rangeOfString:@"Mic" options:0 range:searchRange]).length != 0)
    {
       // hitRange contains the range we found "Mic". We have to enlarge it on both sides to \n
       NSRange lineRange = [source rangeOfString:@"\n" options:NSBackwardSearch range:NSMakeRange( 0, hitRange.location ); // Maybe it is an optimization to cut the range ahead of the hit to the last hit. But since it is a backward search, I do not think that this has any effect.
    
       // We can enlarge the range to the prev \n:
       hitRange.length = hitRange.range + hitRange.location - lineRange.location +1;
    
       // Go the the next \n
       lineRange = [source rangeOfString:@"\n" options:0 range:NSMakeRange( hitRange.location+hitRange.length, length-hitRange.location+hitRange.length)];
    
       hitRange.length = lineRange.location-hitRange.location;
    
       // Do something with the line
       …
    
       // Move forward
       searchRange.location = hitRange.location+hitRange.length;
       searchRange.length = length-searchRange.location;
    }
    

    Typed in Safari, just to show the approach. Definetly there are +1/-1 errors in it and some optimizations to reduce the search for single chars.

    Obviously this works only, if there are \n at the end and beginning. Add \n if not so.

    I really do not know, whether this is faster or not. However, it is worth a try.

    As an addition you can make a method in a category of NSString out of it taking a block that takes the range. This would make it more generic.

    Modified Code:

    NSUInteger length = [dataToBeParsedFirst length];
    NSRange searchRange = NSMakeRange( 0, length );
    NSRange hitRange = NSMakeRange( 0, 0 );
    NSMutableArray *linesArr =[[NSMutableArray alloc]init];
    
    // Iterate over the string unless you do not find any hit
    while( (hitRange = [dataToBeParsedFirst rangeOfString:ItemID options:0 range:searchRange]).length != 0)
    {
        NSRange lineRange = [dataToBeParsedFirst rangeOfString:@"\n" options:NSBackwardsSearch range:NSMakeRange( 0, hitRange.location )];
    
        NSRange lineEndRange = [dataToBeParsedFirst rangeOfString:@"\n" options:0 range:NSMakeRange(hitRange.location, 500)];
    
        NSRange dataRange = NSMakeRange(lineRange.location + lineRange.length, lineEndRange.location - lineRange.location + lineRange.length - 1);
    
        [linesArr addObject:[[dataToBeParsedFirst substringWithRange:dataRange] substringToIndex:dataRange.length - 1]];
    
        // Move forward
        searchRange.location = lineEndRange.location+lineEndRange.length;
        searchRange.length = length-searchRange.location;
    }
    
    NSString *dataToBeParsed = [[linesArr componentsJoinedByString:@"\n"] stringByAppendingString:@"\n"];