Reputation: 500
I'm having a problem when I want to create string from a index of string that contains a special character. I'll post you a playground example.
var str = """
circular para poder realizar sus tareas laborales correspondientes a las actividades de comercialización de alimentos
"""
let regex = try? NSRegularExpression(pattern: ".", options: .caseInsensitive)
let results = regex?.matches(in: str, options: .withoutAnchoringBounds, range: NSRange(0..<str.count - 1))
results?.forEach { result in
let newStr = String(str[Range(result.range, in: str)!])
print(newStr)
}
Now I get an error when the range of the character "ó" wants to form a string. How can I solve this?
Thanks
Upvotes: 0
Views: 717
Reputation: 539775
There are two problems. One is that the NSRange
must be created from the count of UTF-16 code units int the string (because that is what NSString
uses), compare Swift extract regex matches. So that should be NSRange(0..<str.utf16.count)
or NSRange(str.startIndex..., in: str)
.
The other problem is that the string uses decomposed Unicode characters. Here is a simplified demonstration:
let str = "ó"
print(Array(str.utf16))
let regex = try? NSRegularExpression(pattern: ".", options: .caseInsensitive)
let results = regex?.matches(in: str, range: NSRange(str.startIndex..., in: str))
results?.forEach { result in
print(result.range, Range(result.range, in: str))
}
// Output:
// [111, 769]
// {0, 1} nil
// {1, 1} nil
NSRegularExpression
is an “old” Foundation method and works on an NSString
. Here that NSString
has two UTF-16 characters, and both are matched by the "." pattern.
The problem is that the returned NSRange
s do not correspond to Swift String
ranges, and therefore Range(result.range, in: str)
returns nil
.
A possible solutions is to normalize the string to use only composite Unicode characters:
let str = "ó".precomposedStringWithCanonicalMapping
Now the string has only a single Unicode character, and only a single NSRange
is returned. The above test program produces the output
// [243]
// {0, 1} Optional(Range(Swift.String.Index(...)))
Upvotes: 2