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

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

Related

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)

Replace matches using regex by modifying matched string in swift

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.

Cut a String from start position to end position with swift 3

I have Strings with the form string \ string example
"some sting with random length\233"
I want to deletes the last \ and get the value after it, so the result will be
"some sting with random length"
"233"
I tried this code but it's not working
let regex = try! NSRegularExpression(pattern: "\\\s*(\\S[^,]*)$")
if let match = regex.firstMatch(in: string, range: string.nsRange) {
let result = string.substring(with: match.rangeAt(1))
}
You did not correctly adapt the pattern from How to get substring after last occurrence of character in string: Swift IOS to your case. Both instances of the comma must be replaced by a backslash,
and that must be "double-escaped":
let regex = try! NSRegularExpression(pattern: "\\\\\\s*(\\S[^\\\\]*)$")
(once be interpreted as a literal backslash in the regex pattern, and
once more in the definition of a Swift string literal).
However, a simpler solution is to find the last occurrence of the
backslash and extract the suffix from that position:
let string = "some sting with random length\\233"
let separator = "\\" // A single(!) backslash
if let r = string.range(of: separator, options: .backwards) {
let prefix = string.substring(to: r.lowerBound)
let suffix = string.substring(from: r.upperBound)
print(prefix) // some sting with random length
print(suffix) // 233
}
Update for Swift 4:
if let r = string.range(of: separator, options: .backwards) {
let prefix = string[..<r.lowerBound]
let suffix = string[r.upperBound...]
print(prefix) // some sting with random length
print(suffix) // 233
}
prefix and suffix are a String.SubSequence, which can be used
in many places instead of a String. If necessary, create a real
string:
let prefix = String(string[..<r.lowerBound])
let suffix = String(string[r.upperBound...])
You could do this with regex, but I think this solution is better:
yourString.components(separatedBy: "\\").last!
It splits the string with \ as the separator and gets the last split.

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

Return range with first and last character in string

I have a string: "Hey #username that's funny". For a given string, how can I search the string to return all ranges of string with first character # and last character to get the username?
I suppose I can get all indexes of # and for each, get the substringToIndex of the next space character, but wondering if there's an easier way.
If your username can contain only letters and numbers, you can use regular expression for that:
let s = "Hey #username123 that's funny"
if let r = s.rangeOfString("#\\w+", options: NSStringCompareOptions.RegularExpressionSearch) {
let name = s.substringWithRange(r) // #username123"
}
#Vladimir's answer is correct, but if you're trying to find multiple occurrences of "username", this should also work:
let s = "Hey #username123 that's funny"
let ranges: [NSRange]
do {
// Create the regular expression.
let regex = try NSRegularExpression(pattern: "#\\w+", options: [])
// Use the regular expression to get an array of NSTextCheckingResult.
// Use map to extract the range from each result.
ranges = regex.matchesInString(s, options: [], range: NSMakeRange(0, s.characters.count)).map {$0.range}
}
catch {
// There was a problem creating the regular expression
ranges = []
}
for range in ranges {
print((s as NSString).substringWithRange(range))
}

Resources