Tel Tel - 4 months ago 32
Swift Question

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
}
}
}

Answer

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
}
Comments