Search code examples
swiftregexswift5

With Swift replacingOccurrences .regularExpression, how can you PROCESS each match (for example, each match becomes uppercase)?


Say you have

    let fixed = t.replacingOccurrences(
        of: #"(?m)"aa[.\s\S]*?bb"#,
        with: ,
        options: [.regularExpression])

You pass in the text of a file, let's say.

This will of course find all the long strings "aa . . . bb" within the text.

If we do this ...

    let fixed = t.replacingOccurrences(
        of: #"(?m)"aa[.\s\S]*?bb"#,
        with: "WOW! $0",
        options: [.regularExpression])

it will replace each of the "aa . . . bb" with "WOW aa . . . bb".

If we do this ...

    let fixed = t.replacingOccurrences(
        of: #"(?m)"aa[.\s\S]*?bb"#,
        with: "$0 $0 $0",
        options: [.regularExpression])

it will replace each of the "aa . . . bb" with three copies of that "aa . . . bb".

What if we want to replace each of the "aa . . . bb" with an UPPERCASED version of "aa . . . bb"

So conceptually something like this ...

    let fixed = t.replacingOccurrences(
        of: #"(?m)"aa[.\s\S]*?bb"#,
        with: "$0".uppercased(),
        options: [.regularExpression])

Of course, that is not how StringProtocol works, it is meaningless.

Thus, I essentially want to do this:

    let fixed = t.replacingOccurrences(
        of: #"(?m)"aa[.\s\S]*?bb"#,
        with: process( ... the actual value of $0 ... ),
        options: [.regularExpression])

Where func process(_ s: String) -> String.

How do you do that, how do you "get at" the value in that pass of the string protocol (ie, of the $0) and then (say) call a function with it, returning a string to use as the result for that pass?


Just FTR, specifically I want to remove all whitespace/newlines from each of the "aa . . . bb". (Example "1 2 3 aa x y z bb 1 2 3 aa p q r bb" becomes "1 2 3 aaxyzbb 1 2 3 aapqrbb"


Solution

  • Use the Swift regex APIs. There are overloads of replacing(_:with:) that take RegexComponents. You can pass a closure that takes a Match, and computes a replacement.

    Example for uppercasing the matches:

    let text = "something else aa foo bar baz bb something else"
    let regex = /(?m)aa[\s\S]*?bb/
    let result = text.replacing(regex, with: { match in
        // you can do whatever you want with "match" here,
        // then return a collection of characters
        match.output.uppercased()
    })
    print(result)
    // something else AA FOO BAR BAZ BB something else