NSSearchField handling \n chars

Discussion in 'Mac Programming' started by Perkin, Dec 21, 2014.

  1. Perkin macrumors newbie

    Oct 22, 2013
    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
    Search text is
    Example text in textview is
    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
    - (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];
  2. chown33 macrumors 604

    Aug 9, 2009
    If I understand your description correctly, then this:
    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.
  3. Perkin thread starter macrumors newbie

    Oct 22, 2013
    Yes, I found that to be the problem, and here's what I came up with for a solution

    changing the line
    NSString *replaceString = [findReplace2Field stringValue];
    to call an unEscapeTemplate function
    NSString *replaceString = [self unEscapeTemplate:[replaceSearchField stringValue]];
    here's the function I used
    -(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;

Share This Page