lakshmen
lakshmen

Reputation: 29064

Searching for the words with brackets placed around them in iOS

I am trying to search for all words with brackets around them in a group of text and change it to italic in iOS. I am using this code to search for brackets in the text:

static inline NSRegularExpression * ParenthesisRegularExpression() {
    static NSRegularExpression *_parenthesisRegularExpression = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _parenthesisRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"\\[[^\\(\\)]+\\]" options:NSRegularExpressionCaseInsensitive error:nil];
    });

    return _parenthesisRegularExpression;
}

I am using this to show me the matches:

NSRange matchRange = [result rangeAtIndex:0];
NSString *matchString = [[self.markerPointInfoDictionary objectForKey:@"long_description"] substringWithRange:matchRange];
NSLog(@"%@", matchString);

But it is returning me all the text from the first [ to the last ] in the group of text. There is a lot brackets in between.

I am using this code to change the text to italic:

-(TTTAttributedLabel*)setItalicTextForLabel:(TTTAttributedLabel*)attributedLabel fontSize:(float)Size
{
    [attributedLabel setText:[self.markerPointInfoDictionary objectForKey:@"long_description"] afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString)
     {
         NSRange stringRange = NSMakeRange(0, [mutableAttributedString length]);
         NSRegularExpression *regexp = ParenthesisRegularExpression();
         UIFont *italicSystemFont = [UIFont italicSystemFontOfSize:Size];
         CTFontRef italicFont = CTFontCreateWithName((__bridge CFStringRef)italicSystemFont.fontName, italicSystemFont.pointSize, NULL);
         [regexp enumerateMatchesInString:[mutableAttributedString string] options:0 range:stringRange usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
             NSRange matchRange = [result rangeAtIndex:0];
             NSString *matchString = [[self.markerPointInfoDictionary objectForKey:@"long_description"] substringWithRange:matchRange];
             NSLog(@"%@", matchString);
             if (italicFont) {
                 [mutableAttributedString removeAttribute:(NSString *)kCTFontAttributeName range:result.range];
                 [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)italicFont range:result.range];
                 CFRelease(italicFont);
                 NSRange range1 = NSMakeRange (result.range.location, 1);
                 NSRange range2 = NSMakeRange (result.range.location + result.range.length -2 , 1);
                 [mutableAttributedString replaceCharactersInRange:range1 withString:@""];
                 [mutableAttributedString replaceCharactersInRange:range2 withString:@""];
             }
         }];
         return mutableAttributedString;
     }];

    return attributedLabel;
}

Btw my Text looks like this:

[hello] welcome [world], my [name] is [lakesh]

The results looks like this:

match string is [hello]
match string is [world]
match string is  is [lak

then crash..

Need some guidance to show me my mistake.

Upvotes: 4

Views: 757

Answers (2)

uchuugaka
uchuugaka

Reputation: 12782

What's your crash log? Are you modifying the string in place with the match ranges from the original unmodified string? If so, you will run out of bounds before finishing.

You can consider a few othe approaches. You need to do the removal one at a time and stop trying to make your code do italics and character replacement in the same place. That is confusing you.

Your italics part is fine. But you need to do your character removal with an additional bit of iteration logic that gets a new match range from the niw shorter string after each character is removed.

Upvotes: 1

herzbube
herzbube

Reputation: 13378

I haven't thought about the code you are showing, but your regular expression seems to be too greedy:

@"\\[[^\\(\\)]+\\]"

This matches 1-n characters between brackets "[]". The characters are defined by a character class that says "all characters except paranthesis", i.e. except "()".

In other words, your character class also matches bracket characters. The result is that your expression matches everything in between the first "[" and the last "]".

I suggest you try the following regular expression instead:

@"\\[[^\\[\\]]+\\]"

If you don't have nested brackets, you could even simplify it to this:

@"\\[[^\\]]+\\]"


EDIT

This is a simplified version of your code and the sample input text you provided, but using the regex that I have suggested. In my environment this works perfectly, it prints 4 lines to my debug output window. I don't know what causes your crash, so I suggest you start to simplify your own code step by step until you have found the problem.

NSString* mutableAttributedString = @"[hello] welcome [world], my [name] is [lakesh]";
NSRange stringRange = NSMakeRange(0, [mutableAttributedString length]);
NSRegularExpression* regexp = [[NSRegularExpression alloc] initWithPattern:@"\\[[^\\]]+\\]" options:NSRegularExpressionCaseInsensitive error:nil];
[regexp enumerateMatchesInString:mutableAttributedString
                         options:0
                           range:stringRange
                      usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
 {
   NSRange matchRange = [result rangeAtIndex:0];
   NSString* matchString = [mutableAttributedString substringWithRange:matchRange];
   NSLog(@"%@", matchString);
 }
 ];

The main differences to your code are:

  • I don't work with TTTAttributedLabel
  • I don't use NSMutableAttributedString
  • I don't use self.markerPointInfoDictionary
  • I don't have the if (italicFont) code block

So your problem should be in one of these areas.

Upvotes: 7

Related Questions