Cue
Cue

Reputation: 3092

How to use Regular Expression in Swift to replace matching expressions with the template string instead of plain text

I have a project with a main textView and two textFields. In the main textView field there is the text to process, in the first textField you enter a regular expression and in the second textField you enter the substitution template string.

If in the second textField I enter text, all work fine, but if I enter some regex it seems that this is handled like plain text. For example entering \n\n instead of adding two newlines characters in the final text, it just enter nn.

Here is my code, it's partially based on

What am I doing wrong?

if let regex =  NSRegularExpression.expresssionWith(theSearchOptions){ // theSearchOptions uses the struct below

    // replace matches
    let regStr = regex.stringByReplacingMatchesInString(str, options: .WithTransparentBounds, range: NSMakeRange(0, str.characters.count), withTemplate: substitution) // EDIT: substitution is the var that contains the template string
    newStr = regStr
}


struct SearchOptions {
    let searchString: String
    var replacementString: String
    let matchCase: Bool
    let wholeWords: Bool
    let escapeMetacharacters: Bool
}

extension NSRegularExpression {
    static func expresssionWith(options: SearchOptions) -> NSRegularExpression? {
        let searchString = options.searchString
        let isCaseSensitive = options.matchCase // true
        let isWholeWords = options.wholeWords // false
        let escapeMetacharacters = options.escapeMetacharacters // false

        // handle case sensitive option
        var regexOption: NSRegularExpressionOptions = .CaseInsensitive
        if isCaseSensitive { // if it is match case remove case sensitive option
            regexOption = []
        }

        // put the search string in the pattern
        var pattern = searchString

        if escapeMetacharacters {
            pattern = NSRegularExpression.escapedPatternForString(searchString)
            // adding backslash escapes as necessary to protect any characters that would match as pattern metacharacters
        } else {
            // no changes in the pattern
        }

        if isWholeWords {
            pattern = "(?<!\\w)" + NSRegularExpression.escapedPatternForString(searchString) + "(?!\\w)"
        }

        do {
            return try NSRegularExpression(pattern: pattern, options: regexOption)
        } catch  {
            print("Error: \(error)")
            return nil
        }
    }
}

Upvotes: 0

Views: 4418

Answers (1)

OOPer
OOPer

Reputation: 47876

As you know textField does not convert user inputs, so entering \n\n will make 4 character String \ n \ n.

And regex templates do not recognize much escaping sequence as compared to regex patterns.

Template Matching Format

Regular Expression Metacharacters

As you see only two metacharacters are defined: $ and \. And \ is defined as:

Treat the following character as a literal, suppressing any special meaning. Backslash escaping in substitution text is only required for '$' and '\', but may be used on any other character without bad effects.

This description means that any character following \ is treated as the character itself. Supplying \n\n as a template is exactly the same as nn.

As Wiktor Stribiżew already commented, if you want to give special meaning to some character sequences, you need to do it yourself.

if let regex =  NSRegularExpression.expresssionWith(theSearchOptions){ // theSearchOptions uses the struct below
    //Replace metacharacters in `substitution` (Assuming `substitution` is a var.)
    substitution = substitution.stringByReplacingOccurrencesOfString("\\n", withString: "\n")
    substitution = substitution.stringByReplacingOccurrencesOfString("\\r", withString: "\r")
    substitution = substitution.stringByReplacingOccurrencesOfString("\\t", withString: "\t")
    //You may need more...

    // replace matches
    let regStr = regex.stringByReplacingMatchesInString(str, options: .WithTransparentBounds, range: NSMakeRange(0, str.characters.count), withTemplate: substitution)
    newStr = regStr
}

Upvotes: 1

Related Questions