A few weeks ago someone posted a pretty interesting question on SO and I decided to have a crack to see how far I could get.
In a nutshell, the OP was looking for an efficient way of finding and replacing URLs in an NSString, and possible use the same method to filter out rude words (available in an existing NSDictionary) from content posted by users.
So I came with with a method that takes a “pattern” (i.e. the word you are looking for), a replacement string (i.e. what you want to replace it with) and a character set which allows you to add variations of the pattern (i.e. punctuation, spaces etc). It is a good case study / example of how to combina NSString, NSRange and NSScanner to manipulate strings in a very flexible way.
The code is posted below with comments – it should be fairly self-explanatory but feel free to ask any questions if you run into problems.
– (NSString*)replacePattern:(NSString*)pattern withReplacement:(NSString*)replacement forString:(NSString*)string usingCharacterSet:(NSCharacterSet*)characterSetOrNil
// Check if a NSCharacterSet has been provided, otherwise use our "default" one
characterSetOrNil = [NSCharacterSet characterSetWithCharactersInString:@" !?,()]"];
// Create a mutable copy of the string supplied, setup all the default variables we’ll need to use
NSMutableString *mutableString = [[[NSMutableString alloc] initWithString:string] autorelease];
NSString *beforePatternString = nil;
NSRange outputrange = NSMakeRange(0, 0);
// Check if the string contains the "pattern" you’re looking for, otherwise simply return it.
NSRange containsPattern = [mutableString rangeOfString:pattern];
while (containsPattern.location != NSNotFound)
// Found the pattern, let’s run with the changes
// Firstly, we grab the full string range
NSRange stringrange = NSMakeRange(0, [mutableString length]);
NSScanner *scanner = [[NSScanner alloc] initWithString:mutableString];
// Now we use NSScanner to scan UP TO the pattern provided
[scanner scanUpToString:pattern intoString:&beforePatternString];
// Check for nil here otherwise you will crash – you will get nil if the pattern is at the very beginning of the string
// outputrange represents the range of the string right BEFORE your pattern
// We need this to know where to start searching for our characterset (i.e. end of output range = beginning of our pattern)
if (beforePatternString != nil)
outputrange = [mutableString rangeOfString:beforePatternString];
// Search for any of the character sets supplied to know where to stop.
// i.e. for a URL you’d be looking at non-URL friendly characters, including spaces (this may need a bit more research for an exhaustive list)
NSRange characterAfterPatternRange = [mutableString rangeOfCharacterFromSet:characterSetOrNil options:NSLiteralSearch range:NSMakeRange(outputrange.length, stringrange.length-outputrange.length)];
// Check if the link is not at the very end of the string, in which case there will be no characters AFTER it so set the NSRage location to the end of the string (== it’s length)
if (characterAfterPatternRange.location == NSNotFound)
characterAfterPatternRange.location = [mutableString length];
// Assign the pattern’s start position and length, and then replace it with the pattern
NSInteger patternStartPosition = outputrange.length;
NSInteger patternLength = characterAfterPatternRange.location – outputrange.length;
[mutableString replaceCharactersInRange:NSMakeRange(patternStartPosition, patternLength) withString:replacement];
// Reset containsPattern for new mutablestring and let the loop continue
containsPattern = [mutableString rangeOfString:pattern];
return [[mutableString copy] autorelease];