Replace every second occurrence of quotes in NSString (iOS NSString)


I need to replace the standard upper double quotes "" that already exist (in text I get from a database) with specific quotes for german text for use in a UIWebView. The first double quote character before the quoted word(s) (every even index) needs to be replaced with the HTML equivalent of „ which is „ and the second one after the quoted word(s) (every odd index) with “ which is “. The quotes can be anywhere in the text, so I can't rely on any fixed positions.

So basically I have a NSString like this:

just "some" text with some "more" text

or in Objective-C:

NSString *str = @"just \"some\" text with some \"more\" text";

I am aware of the stringByReplacingOccurrencesOfString method from NSString, but of course this only replaces all double quotes with the bottom double quote mark.

NSString *newStr = [str stringByReplacingOccurrencesOfString:@"\"" withString:@"„"];

I couldn't find a way to just replace every second or every n-th occurrence, does someone have a hint/idea how I could accomplish that?

Thanks a lot



NSScanner offers another option...

NSScanner *scanner = [NSScanner scannerWithString:englishString];
NSMutableString *germanString = [NSMutableString string];
BOOL foundQuote = YES;
int quoteIndex = 0;

while (foundQuote) {
    NSString *nextPart = @"";
    [scanner scanUpToString:@"\"" intoString:&nextPart];
    if (nextPart != nil) {
        [germanString appendString:nextPart];
    }
    foundQuote = [scanner scanString:@"\"" intoString:nil];
    if (foundQuote) {
        [germanString appendString:((quoteIndex % 2) ? @"“" : @"„")];
        quoteIndex++;
    }
}

NSLog(@"The German version is: %@", germanString);

I would suggest using regular expressions then.

NSString *str = @"just \"some\" text with some \"more\" text";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\"(\\w+)\"" options:NSRegularExpressionCaseInsensitive error:nil];
NSString *returnString = [regex stringByReplacingMatchesInString:str options:0 range:NSMakeRange(0, [str length]) withTemplate:@"\"$1&quote"];
NSLog(@"%@",returnString);

something like this should be a push in the right direction:

static NSString * StringByReplacingEverySecondOccurrenceWithString(
                        NSString * const pSource,
                        NSString * const pSearch,
                        NSString * const pReplace)
{
  /* @todo test that pSource has two occurrences before copying, and return [pSource.copy autorelease] if false. */
  NSMutableString * const str = [pSource.mutableCopy autorelease];
  bool isEven = true;
  for (NSUInteger pos = 0; pos < str.length; isEven = !isEven) {
    const NSRange remainder = NSMakeRange(pos, str.length - pos);
    const NSRange next = [str rangeOfString:pSearch options:0 range:remainder];
    if (NSNotFound != next.location && !isEven) {
      [str replaceCharactersInRange:next withString:pReplace];
    }
    pos = next.location + next.length;
  }
  return [str.copy autorelease];
}

Update

and if you want to follow Caleb's edit to the question, you can use this to alternate substituted strings:

static NSString * StringByReplacingWithAlternatingStrings(
                          NSString * const pSource,
                          NSString * const pSearch,
                          NSString * const pReplaceA,
                          NSString * const pReplaceB)
{
  /* @todo test that pSource has two occurrences before copying, and return [pSource.copy autorelease] if false. */
  NSMutableString * const str = [pSource.mutableCopy autorelease];
  bool isEven = true;
  for (NSUInteger pos = 0; pos < str.length; isEven = !isEven) {
    const NSRange remainder = NSMakeRange(pos, str.length - pos);
    const NSRange next = [str rangeOfString:pSearch options:0 range:remainder];
    if (NSNotFound != next.location) {
      NSString * const substitution = isEven ? pReplaceA : pReplaceB;
      [str replaceCharactersInRange:next withString:substitution];
      pos = next.location + substitution.length;
    }
    else {
      pos = NSNotFound;
    }
  }
  return [str.copy autorelease];
}