Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Perkin

macrumors newbie
Original poster
Oct 22, 2013
16
0
I'm trying to do a regular expression replace all function, getting the search and replace text from a NSSearchField, but I can't seem to manage using \n in the replace.

I must be missing something obvious.

Any ideas on how to do what I want?

Replace text is
Code:
$1\n\n$2

Search text is
Code:
([a-z])([A-Z])

Example text in textview is
Code:
just a sampleShould be broken

if I use 1, 2 or 4 '\' chars to escape the replace still doesn't actually work, properly - 1 inserts 'n', 2 inserts '\n' and 4 inserts '\\n'

None actually replace with a newline character

If I alter the function to use directly @"$1\n\n$2" (instead of getting same text from NSSearchField) then it would work as expected

The Action/function is as follows
Code:
- (IBAction)clickedReplaceAll:(id)sender {
    NSString *searchString = [findSearchField stringValue];
    NSString *replaceString = [findReplace2Field stringValue];
    
    NSError *error;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:searchString options:NSRegularExpressionAnchorsMatchLines error:&error];
    
    NSString *allText = _textview.string;
    NSString *modifiedString = [regex stringByReplacingMatchesInString:allText options:0 range:NSMakeRange(0, [allText length]) withTemplate: replaceString];
    
    [_textview setString:modifiedString];
}
 

chown33

Moderator
Staff member
Aug 9, 2009
10,743
8,417
A sea of green
If I understand your description correctly, then this:
If I alter the function to use directly @"$1\n\n$2" (instead of getting same text from NSSearchField) then it would work as expected
is exactly what I would expect to happen.

It happens because the literal-NSString parsing code in the compiler understands backslash sequences. So what it compiles is an NSString literal that contains two LF (0x0A) characters. It DOES NOT compile to a string containing any actual backslashes or 'n' characters.

Conversely, if you type in the text "$1\n\n$2", then it consists of actual backslash characters (0x5C) with immediately following 'n' characters. AFAICT, the replace capability of NSRegularExpression has a very small number of metacharacters in templates.

Refer to the class docs and look at what metacharacters are supported in the replacement template (subheading "Template Matching Format"). It's basically $ to signify a numbered capture group, and \ to remove the meta-significance of $ (and \ itself). Everything else in the template string is a literal (i.e. the character represents itself). Replacement templates have nowhere near the number of backslash-escaped metacharacters that pattern strings have available.

To do what you want, you need to convert the typed-in string containing literal backslashes into one containing literal newlines. You can do this any number of ways, including by applying a NSRegularExpression and doing replacement on characters that follow a backslash. Make sure you treat a typed-in substring of "\$" correctly.

If some of that is confusing, I suggest performing a hex dump of the characters in the replacement template in both the string-literal case and the typed-in case.
 

Perkin

macrumors newbie
Original poster
Oct 22, 2013
16
0
Yes, I found that to be the problem, and here's what I came up with for a solution

changing the line
Code:
NSString *replaceString = [findReplace2Field stringValue];
to call an unEscapeTemplate function
Code:
NSString *replaceString = [self unEscapeTemplate:[replaceSearchField stringValue]];

here's the function I used
Code:
-(NSString *)unEscapeTemplate:(NSString*)template {
    NSMutableString* modified = template.mutableCopy;
    NSDictionary *escapes = @{ @"\\0": @"\0", @"\\a": @"\a", @"\\b": @"\b",
                               @"\\e": @"\e", @"\\f": @"\f", @"\\n": @"\n",
                               @"\\r": @"\r", @"\\t": @"\t", @"\\v": @"\v"};
    for (id key in escapes) {
        if ([modified containsString:key]) {
            [modified replaceOccurrencesOfString:key withString:escapes[key] options:0 range:NSMakeRange(0, [modified length])];
        }
    }
    return modified;
}
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.