江戸一番のジャスタウェイ職人のブログ

江戸一番のジャスタウェイ職人

NSRegularExpressionやNSDataDetectorで指定するrangeにはString.utf16.countを使う

テキストからリンクを抜き出す場合などよくこういったコードを見ますが、テキストにサロゲートペアが含まれているとマッチの対象がサロゲートペアの分短く解釈され、末尾が切れたりします。

let text = "🍣 is sushi http://example.com/hello"
let detector = try! NSDataDetector(types: NSTextCheckingType.Link.rawValue)
let matches = detector.matchesInString(text,
                                       options: [],
                                       range: NSRange.init(location: 0, length: text.characters.count))

for match in matches {
    let link = (text as NSString).substringWithRange(match.range)
    print(link) // -> http://example.com/hell
}

String.utf16.count を使うと期待した結果を得ることが出来ます。

let text = "🍣 is sushi http://example.com/abc"
let detector = try! NSDataDetector(types: NSTextCheckingType.Link.rawValue)
let matches = detector.matchesInString(text,
                                       options: [],
                                       range: NSRange.init(location: 0, length: text.utf16.count))

for match in matches {
    let link = (text as NSString).substringWithRange(match.range)
    print(link) // -> http://example.com/hello
}

しかしこれは本当に気づきにくくSwiftライブラリでも characters.count を利用している例が多いです。