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
            )
7
  • 1
    .uppercased will make the literal "$1" uppercased, not the captured group. Commented Nov 1, 2021 at 7:19
  • how can I apply . uppercased for captured value? Commented Nov 1, 2021 at 7:22
  • 2
    If I knew I would have told you. Just wanted to let you know why your current solution doesn't work Commented Nov 1, 2021 at 7:23
  • 1
    I think you need to work with Range here so you can manually uppercase each match, see for instance this article Commented Nov 1, 2021 at 7: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? Commented Nov 1, 2021 at 8:13

3 Answers 3

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

Sign up to request clarification or add additional context in comments.

2 Comments

Your method will result in an infinite loop if the resulting range of the regex represents an empty string. Check this post for a proper implementation which supports substrings as well
@LeoDabus Thanks, I didn't think about that edge case
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)

Comments

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

4 Comments

thanks for answering. but if occurrence is like ^he\d+, it doesn't work.
You can’t lowercase the whole string, this might change other characters that shouldn’t be replaced.
oh i seem, in that case we can change the option to .caseInsensitive and remove the .lowercased() correct?
Yes but you still need to supply a proper regex, now you only have a plain string. And this is will never work using replacingOccurrenses as far as I know

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.