今回は、NSDataDetector
を使って特定の文字列を抽出する時に少しハマったポイントを紹介したいと思います。
ハマりポイント
いつもの如く NSDataDetector
を使用して、文字列の中に存在するリンクを下記のように取得していると、意図しない場所でリンクがきれて読み込まれてしまうという問題が発生しました。
func getLinkTextList(text: String) -> [String] { guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { return [] } let enableLinkTuples = detector.matches(in: text, range: NSRange(location: 0, length: text.count)) return enableLinkTuples.map { checkingResult -> String in return (text as NSString).substring(with: checkingResult.range) } } let linkString = getLinkTextList(text: "僕の記事はこちら🙃👨<200d>💻🍎✨🎣 https://yamato8010.hatenablog.com/").first! print(linkString) // 出力: https://yamato8010.hatenabl <- 変なところできれてる!
原因としては、絵文字などが入っている場合に、NSDataDetector がパースする際の文字列サイズと、text.count
で取得していた文字列のサイズが合わずに途中までしかリンクが読み込まれないというものでした✍️
恐らく、NSString が、UTF-16 コードユニットのシーケンスとして表示されるため、Objective-C 由来の NSDataDetector も UTF-16 でパースされるのが原因かと思われます🤔
解決
text.count
を text.utf16.count
に変えて実行すると正常にリンクを表示することができるようになりました🎉
func getLinkTextList(text: String) -> [String] { guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { return [] } let enableLinkTuples = detector.matches(in: text, range: NSRange(location: 0, length: text.utf16.count)) return enableLinkTuples.map { checkingResult -> String in return (text as NSString).substring(with: checkingResult.range) } } let linkString = getLinkTextList(text: "僕の記事はこちら🙃👨<200d>💻🍎✨🎣 https://yamato8010.hatenablog.com/").first! print(linkString) // 出力: https://yamato8010.hatenablog.com/
余談
文字コードに下記の記事を参考にまとめました✍️
https://qiita.com/yamoridon/items/6f73ffe02ab46eb6ae85
property | 内容 |
---|---|
String.utf16.count | 2バイトを1単位として、1つの文字番号を1単位(2バイト)または2単位(4バイト)の可変長で文字サイズ(単位)を取得します。NSString などの文字サイズはこの utf16 での単位数になります。 |
String.unicodeScalars.count | 文字のサイズを utf32 で表します。utf32 は1単位を4バイトの固定長として文字番号を扱えるようにしたもので、これにより全ての文字番号を1単位で扱えるようになります。 |
String.utf8.count | 英数文字のような頻繁に使用される文字は1バイトで、マイナーな文字を複数バイトで扱うようにすることで、1バイトを1単位として、1~4単位の可変長で文字サイズを取得できるようにします。これによって、容量を無駄に使用する英数字などを使う際に容量を無駄に使う必要がなくなります。 |
String.count | UI に表示される見た目の文字数で、Unicode によって Grapheme Cluster として定義されている形式 |
参考
- https://developer.apple.com/documentation/foundation/nsstring
- https://developer.apple.com/documentation/foundation/nsdatadetector
- https://qiita.com/yamoridon/items/6f73ffe02ab46eb6ae85#utf-16
- https://aska.hatenablog.com/entry/2016/05/13/161351