Replace matches using regex by modifying matched string in swift - ios

Im trying to replace matched strings using regex in swift, my requirement is as below
originalString = "It is live now at Germany(DE)"
i want the string within the (" ") i.eDE to be separated by space i.e. "D E"
so replacedString should be "It is live now at Germany(D E)"
i tried below code
var value: NSMutableString = "It is live now at Germany(DE)"
let pattern = "(\\([A-Za-z ]+\\))"
let regex = try? NSRegularExpression(pattern: pattern)
regex?.replaceMatches(in: value, options: .reportProgress, range:
NSRange(location: 0,length: value.length), withTemplate: " $1 ")
print(value)
output is It is live now at Germany (DE), i know it's not what is required.
here it is based on the template where we cannot modify based on matched string value. Is there any way to achieve this ?
Thanks in advance

You may use
var value: NSMutableString = "It is live now at Germany(DE) or (SOFE)"
let pattern = "(?<=\\G(?<!\\A)|\\()[A-Za-z](?=[A-Za-z]+\\))"
let regex = try? NSRegularExpression(pattern: pattern)
regex?.replaceMatches(in: value, options: .reportProgress, range: NSRange(location: 0,length: value.length), withTemplate: "$0 ")
print(value)
Or just
let val = "It is live now at Germany(DE) or (SOFE)"
let pattern = "(?<=\\G(?<!\\A)|\\()[A-Za-z](?=[A-Za-z]+\\))"
print( val.replacingOccurrences(of: pattern, with: "$0 ", options: .regularExpression, range: nil) )
Output: It is live now at Germany(D E) or (S O F E)
Pattern details
(?<=\\G(?<!\\A)|\\() - a positive lookbehind that matches a location right after ( or at the end of the preceding successful match
[A-Za-z] - matches and consumes any ASCII letter
(?=[A-Za-z]+\\)) - a positive lookahead that matches a location that is immediately followed with 1+ ASCII letters and then a ) char.
The $0 in the replacement inserts the whole match value back into the resulting string.

Related

Convert placeholders such as %1$s to {x} in Swift

I'm parsing an XML doc (using XMLParser) and some of the values have php-like placeholders, e.g. %1$s, and I would like to convert those to {x-1}.
Examples:
%1$s ---> {0}
%2$s ---> {1}
I'm doing this in a seemingly hacky way, using regex:
But there must be a better implementation of this regex.
Consider a string:
let str = "lala fawesfgeksgjesk 3rf3f %1$s rk32mrk3mfa %2$s fafafczcxz %3$s czcz $#$##%## %4$s qqq %5$s"
Now we're going to extract the integer strings between strings % and $s:
let regex = try! NSRegularExpression(pattern: "(?<=%)[^$s]+")
let range = NSRange(location: 0, length: str.utf16.count)
let matches = regex.matches(in: str, options: [], range: range)
matches.map {
print(String(str[Range($0.range, in: str)!]))
}
Works quite fine. The issue is that the "4" value got mixed up because of the preceding random strings before the %4$s.
Prints:
1
2
3
## %4
5
Is there any better way to do this?
This might not be a very efficient (or swifty :)) way but it gets the job done. What it does is that it searches for a given reg ex and uses the matched substring to extract the numeric value and decrease it and then perform a simple replace between the substring and a newly constructed placeholder value. This is executed in a loop until no more matches are found.
let pattern = #"%(\d*)\$s"#
while let range = str.range(of: pattern, options: .regularExpression) {
let placeholder = str[range]
let number = placeholder.trimmingCharacters(in: CharacterSet(charactersIn: "0123456789.").inverted)
if let value = Int(number) {
str = str.replacingOccurrences(of: placeholder, with: "{\(value - 1)}")
}
}

Swift Regex to allow only uppercase letters and numbers mixed

In my case, I need to Implement Regex for my UITextField. Here, my textfield should allow only uppercase with number mixed values.
For Example:
AI1234
ER3456
I used below one, but not working
^[A-Z0-9]{3}?$
This regex matches the pattern above
2 Uppercase characters followed by 4 numbers
^[A-Z]{2}\\d{4}
You can test it on https://regexr.com/
Edit:
let str = """
AI1234
ER3456
"""
let pattern = try? NSRegularExpression(pattern: "[A-Z]{2}\\d{4}", options: [])
let range = NSRange(location: 0, length: str.utf16.count)
let matches = pattern?.matches(in: str, options: [], range: range)
print(matches)

Exclude pattern in NSRegularExpression

Imagine the following sentence:
The **quick** brown **fox** ...
If I run the following regex
let boldPattern = "\\*{2}([\\w ]+)\\*{2}"
let boldRegex = try NSRegularExpression(pattern: boldPattern)
let str = "The **quick** brown **fox** ..."
let results = regex.matches(in: str, range: NSRange(str.startIndex..., in: str))
results.forEach {
print("$0")
}
I'm able to get all the words between the **.
I have read about negative regex, which returns every words except the ones we're trying to "avoid".
Given the above sentence, is there a way to use a negative regex to get words that doesn't match the boldPattern, so I would get The brown ..., avoiding the **words** ??
Edit
I'm looking for something around this pattern ((?!\\*{2}([\\w ]+)\\*{2}).*) , something that searches for words that doesn't start with ** word **
One way to do it is to use NSRegularExpression's stringByReplacingMatches method.
Example:
let boldPattern = "\\*{2}([\\w ]+)\\*{2}"
let boldRegex = try NSRegularExpression(pattern: boldPattern)
let str = "The **quick** brown **fox** ..."
let unmatchedString = boldRegex.stringByReplacingMatches(in: str, options: [], range: NSMakeRange(0, str.count), withTemplate: "")
print(unmatchedString)
// prints: The brown ...

How to find multiple substrings within braces from a string?

I have a string [Desired Annual Income] /([Income per loan %] /100)
Using this string, I have to find two sub strings 'Desired Annual Income' and 'Income per loan %' in Swift3.
I am using below code to achieve this 'How do I get the substring between braces?':
let myString = "[Desired Annual Income] /([Income per loan %] /100)"
let start: NSRange = (myString as NSString).range(of: "[")
let end: NSRange = (myString as NSString).range(of: "]")
if start.location != NSNotFound && end.location != NSNotFound && end.location > start.location {
let result: String = (myString as NSString).substring(with: NSRange(location: start.location + 1, length: end.location - (start.location + 1)))
print(result)
}
But as an output I am getting only 'Desired Annual Income', How can I get all substrings?
Try this,
Hope it will work
let str = "[Desired Annual Income] /([Income per loan %] /100)"
let trimmedString = str.components(separatedBy: "]")
for i in 0..<trimmedString.count - 1{ // not considering last component since it's of no use hence count-1 times loop
print(trimmedString[i].components(separatedBy: "[").last ?? "")
}
Output:-
Desired Annual Income
Income per loan %
It's a very good use case for regular expressions (NSRegularExpression). The principle of regular expressions is to describe a "pattern" that you want to search in a string.
In that case you search something between two brackets.
The code is then:
let str = "[Desired Annual Income] /([Income per loan %] /100)"
if let regex = try? NSRegularExpression(pattern: "\\[(.+?)\\]", options: [.caseInsensitive]) {
var collectMatches: [String] = []
for match in regex.matches(in: str, options: [], range: NSRange(location: 0, length: (str as NSString).length)) {
// range at index 0: full match (including brackets)
// range at index 1: first capture group
let substring = (str as NSString).substring(with: match.range(at: 1))
collectMatches.append(substring)
}
print(collectMatches)
}
For the explanation about regular expressions, there are plenty of tutorial on internet. But in very short:
\\[ and \\]: opening and closing brackets characters (the double backslashes are because brackets have a meaning in regular expression, so you need to escape them. In a text editor one backslash is enough, but you need a second one because you are in a String and you need to escape the backslash to have a backslash.
(.+?) is a bit more complex: the parentheses are the "capture group", what you want to get. . means "any character", + one or more time, ? after a + is the greedy operator, which means that you want the capture to stop ASAP. If you don't put it, your capture can be in your case "Desired Annual Income] /([Income per loan %", depending of the regex library that you are using. Foundation seems to be greedy by default, that being said.
Regex are not always super easy/direct, but if you do often text processing, it's a very powerful tool to know.

regex to get "words" delimited by space(s)?

What would the regex be (to be used in IOS, "NSRegularExpression") to get the "words" from a string delimited by a space(s), i.e. could be " ", or " ", or " " etc as the delimited.
So therefore:
"26:43:33 S 153:02:51 E"
Would give:
1-"26:43:33"
2-"S"
3-"153:02:51"
4-"E"
So therefore:
"26:43:33 S 153:02:51 E"
Would give:
1-"26:43:33"
2-"S"
3-"153:02:51"
4-"E"
So if you're going to use a regex for this, you want to look for all contiguous stretches of not-space. Like this:
let s = "26:43:33 S 153:02:51 E" as NSString
let pattern = "[^ ]+"
let reg = try! NSRegularExpression(pattern: pattern, options: [])
let matches = reg.matchesInString(s as String, options: [], range: NSMakeRange(0, s.length))
let result = matches.map {s.substringWithRange($0.range)}
// result is: ["26:43:33", "S", "153:02:51", "E"]
As an alternative to regex, I would suggest using the split method on your string.
let string = "26:43:33 S 153:02:51 E"
let words = string.characters.split { $0 == " " }.map { String($0) }
Because calling split on the characters property will return an array of Character types, we need to use the map method to convert them back to strings. map will perform a closure on each element of a collection. In this case we just use it to cast each element to a String

Resources