0

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

Asperi
  • 123,447
  • 8
  • 131
  • 245

1 Answers1

2

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 NSRanges 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(...)))
Martin R
  • 488,667
  • 78
  • 1,132
  • 1,248