0

I am struggling to modify captured value with regex.

For example, I wanna change "Hello, he is hero" to "HEllo, HE is HEro" using Regex.

I know there are ways to change this without regex, but it is just an example to show the problem. I actually use the regex instead of just he, but I cannot provide it here. That is why using regex is required.

The code below somehow does not work. Are there any ways to make it work?

"Hello, he is hero".replacingOccurrences(
                of: #"(he)"#,
                with: "$1".uppercased(), // <- uppercased is not applied
                options: .regularExpression
            )
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
Pytan
  • 1,138
  • 11
  • 28
  • 1
    .uppercased will make the literal `"$1"` uppercased, not the captured group. – Marc Nov 01 '21 at 07:19
  • how can I apply . uppercased for captured value? – Pytan Nov 01 '21 at 07:22
  • 2
    If I knew I would have told you. Just wanted to let you know why your current solution doesn't work – Marc Nov 01 '21 at 07:23
  • 1
    I think you need to work with Range here so you can manually uppercase each match, see for instance [this article](https://nshipster.com/swift-regular-expressions/) – Joakim Danielson Nov 01 '21 at 07:27
  • `using regex is required` - why? You need an output, any kind of code that accomplishes this should be ok, why do you specifically need a regex soution? – Cristik Nov 01 '21 at 08:13
  • it is just an example. I actually using regex instead of just `he`. – Pytan Nov 01 '21 at 08:55
  • So you need to uppercase the parts of the strings that match a given regex? Is this your goal? – Cristik Nov 01 '21 at 09:05

3 Answers3

2

You need to use your regex in combination with Range (range(of:)) to find matches and then replace each found range separately

Here is a function as an extension to String that does this by using range(of:) starting from the start of the string and then moving the start index to match from forward to after the last match. The actual replacement is done inside a separate function that is passed as an argument

extension String {
    func replace(regex: String, with replace: (Substring) -> String) -> String {
        var string = self
        var startIndex = self.startIndex
        let endIndex = self.endIndex

        while let range = string.range(of: regex, options: [.regularExpression] , range: startIndex..<endIndex) {

            if range.isEmpty {
                startIndex = string.index(startIndex, offsetBy: 1)
                if startIndex >= endIndex { break }
                continue
            }
            string.replaceSubrange(range, with: replace(string[range]))

            startIndex = range.upperBound
        }
        return string
    }
}

Example where we do an case insensitive search for words starting with "he" and replace each match with the uppercased version

let result = "Hello, he is hero. There he is".replace(regex: #"(?i)\bhe"#) {
    $0.uppercased() 
}

Output

HEllo, HE is HEro. There HE is

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
  • 2
    Your method will result in an infinite loop if the resulting range of the regex represents an empty string. Check this [post](https://stackoverflow.com/a/32306142/2303865) for a proper implementation which supports substrings as well – Leo Dabus Nov 02 '21 at 02:49
  • @LeoDabus Thanks, I didn't think about that edge case – Joakim Danielson Nov 02 '21 at 10:32
0

You can try NSRegularExpression. Something like:

import Foundation

var sourceStr = "Hello, he is hero"
let regex = try! NSRegularExpression(pattern: "(he)")
let matches = regex.matches(in: sourceStr, range: NSRange(sourceStr.startIndex..., in: sourceStr))
regex.enumerateMatches(in: sourceStr, range: NSRange(sourceStr.startIndex..., in: sourceStr)) { (match, _, _) in
            guard  let match = match else { return }
            guard let range = Range(match.range, in: sourceStr) else { return }
            let sub = sourceStr[range]
            sourceStr = sourceStr.replacingOccurrences(of: sub, with: sub.uppercased(), options: [], range: range)
    }

print(sourceStr)
Paul B
  • 3,989
  • 33
  • 46
-1

this is the solution i can provide

var string = "Hello, he is hero"
let occurrence = "he"

string = string.lowercased().replacingOccurrences(
    of: occurrence,
    with: occurrence.uppercased(),
    options: .regularExpression
)
print(string)