Posts Tagged ‘UTF-16’
絵文字を含むNSStringの正確な文字数をカウントする(2)
ちょっと前に絵文字の混在した文字列のカウント方法について書いたら、ちゃんとカウントできない文字があると指摘された。
なるほど、確かにカウントできない文字がある。いったいどんな規則性があるのかと思っていたら、絵文字の文字コードをまとめていたサイトを教えてもらった。
それがこれ↓
iOS Emoji
う~ん、まるで規則性が見当たらないと思っていたらピンときた。前は UTF8String
とかやってたけど、内部の文字コードはもしかしてUTF-16ではなかろうか?と思って調べたらやっぱりUTF-16だった。それならわざわざUTF-8に変換しないでUTF-16のまま処理した方が良い。
UTF-16といえば主要なトピックはサロゲートペアだ。0xD800-0xDBFFが上位サロゲート、0xDC00-0xDFFFが下位サロゲート。( Wikipedia#Unicode 参考)なので上位サロゲートを検出したら1文字スキップする。(1)
さらに、国旗の絵文字には Regional Indicator という専用のシンボルが2つの組み合わせで使われている。( Unicode.orgのこのページ の最下段に記載されている。)なのでRegional Indicatorを検出して次のサロゲートペアもRegional Indicatorだったら3文字スキップする。(2)
あと、ガラケーではお馴染みの四角で囲まれた数字には通常の数字の後に COMBINING ENCLOSING KEYCAP という専用の文字が配置されている。だがこれは単純に無視すればいいだろう。(3)
そうすると、こんな感じで書けばいいかな?ちゃんと実装するには文字のインデックスが out of bounds にならんようにした方がいいかも。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | - (NSUInteger) actualNSStringSize:(NSString *) txt { int len = [txt length]; NSUInteger count = 0; for (NSUInteger i = 0; i < len; i++) { unichar c = [txt characterAtIndex:i]; if (0xD83C == c) { unichar c1 = [txt characterAtIndex:i+1]; if ((0xDDE6 <= c1) && (c1 <= 0xDDFF)) { unichar c2 = [txt characterAtIndex:i+2]; if (0xD83C == c2) { unichar c3 = [txt characterAtIndex:i+3]; if ((0xDDE6 <= c3) && (c3 <= 0xDDFF)) { // 国旗なのでスキップ・・・(2) i += 3; ++count; continue; } } } i++; ++count; } else if (0xD800 <= c && c <= 0xDBFF) { // 上位サロゲート・・・(1) i++; ++count; } else if (0xDC00 <= c && c <= 0xDFFF) { // 下位サロゲートなので念のため無視 } else if (0x20E3 == c) { // 囲みなので無視・・・(3) } else { ++count; } } return count; } |
ちなみに、これらの絵文字はiOS独自の実装かと思っていたらそうではなく、Unicode6.0 というれっきとした国際規格なのだそうだ。ガラケーに端を発した日本発の絵文字が知らない間に国際規格に…っていうか Unicode のバージョンって知らない間にこんなに上がっていたんだ。