Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
文字数の話 〜Unicodeの楽しい話〜
Search
しゅん🌙
July 02, 2023
Programming
0
210
文字数の話 〜Unicodeの楽しい話〜
プログラミングをする際に間違えがちな文字数のカウント方法について,Unicodeの仕組みを紐解きながら解説しています.
しゅん🌙
July 02, 2023
Tweet
Share
More Decks by しゅん🌙
See All by しゅん🌙
Rustで対戦型Tetrisを作った話
shunshobon
0
300
文字数の話の続き 〜Unicodeの楽しくない話〜
shunshobon
0
130
Haskellの並列・並行処理
shunshobon
1
290
Other Decks in Programming
See All in Programming
快速入門可觀測性
blueswen
0
500
Stackless и stackful? Корутины и асинхронность в Go
lamodatech
0
1.3k
Fixstars高速化コンテスト2024準優勝解法
eijirou
0
190
ある日突然あなたが管理しているサーバーにDDoSが来たらどうなるでしょう?知ってるようで何も知らなかったDDoS攻撃と対策 #phpcon.2024
akase244
2
7.7k
CQRS+ES の力を使って効果を感じる / Feel the effects of using the power of CQRS+ES
seike460
PRO
0
240
QA環境で誰でも自由自在に現在時刻を操って検証できるようにした話
kalibora
1
140
선언형 UI에서의 상태관리
l2hyunwoo
0
270
オニオンアーキテクチャを使って、 Unityと.NETでコードを共有する
soi013
0
370
php-conference-japan-2024
tasuku43
0
430
Package Traits
ikesyo
1
210
週次リリースを実現するための グローバルアプリ開発
tera_ny
1
1.2k
Swiftコンパイラ超入門+async関数の仕組み
shiz
0
170
Featured
See All Featured
jQuery: Nuts, Bolts and Bling
dougneiner
62
7.6k
We Have a Design System, Now What?
morganepeng
51
7.3k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
StorybookのUI Testing Handbookを読んだ
zakiyama
28
5.4k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
Fantastic passwords and where to find them - at NoRuKo
philnash
50
2.9k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
7
570
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.4k
Music & Morning Musume
bryan
46
6.3k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
28
2.2k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
44
9.4k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
27
1.5k
Transcript
文字数の話 Unicodeの楽しい話
自己紹介 名前: しゅん Twitter: @shun_shobon / GitHub: @shun-shobo1
学校: 長野高専 電子情報工学科 5 得意: Web Frontend / Web Frontend Ops / A11y なI 研究: ホログラフィ・ヒューマンインタフェースなI 趣味: PCゲーム・自作キーボー 一言: コンピュータと人との関わり方を模索しています
突然ですがクイズです
このJSのコードは何を出力する?
答え
このJSのコードは何を出力する?
答え どう見ても11文字しかないのに出力は12......
今回はこの挙動を理解しよう! というコンセプトです
Unicodeの仕組み
Unicodeの目的 Unicodeは全ての文字にユニークなIDを降ること目的としている. このユニークなIDのことをUnicodeでは Code Point(コードポイント) と呼ぶ. チ → U+30C1 ゃ
→ U+3083 𠮟 → U+20B9F ! → U+FF01
Code Pointの符号化方法 このCode Pointを相手に送る際には,特定の方式によってバイナリにすることで送信される. これにはいくつかの方式がある. UTF-32 … どのCode Pointも4Byteで表r
UTF-16 ... 小さなCode Pointは2Byte,大きいのは4Byt UTF-8 ... なるべく小さなByteになるように1〜4Byteで表す ※ものすごい雑な解説なので詳しくは調べてください
符号化の例① 試しに「チ(U+30C1)」をそれぞれの形式で符号化すると... ※UTF-32・UTF-16はビッグエンディアンでの場合 g UTF-32 … 0x00, 0x00, 0x30, 0xCb
g UTF-16 ... 0x30, 0xCb g UTF-8 ... 0xE3, 0x83, 0x81
符号化の例② 試しに「𠮟(U+20B9F)」をそれぞれの形式で符号化すると... I UTF-32 … 0x00, 0x02, 0x0B, 0x9V I
UTF-16 ... 0xD8, 0x42, 0xDF, 0x9V I UTF-8 ... 0xF0, 0xA0, 0xAE, 0x9F ※UTF-32・UTF-16はビッグエンディアンでの場合
先程の挙動の解説
JavaScriptでの内部表現はUTF-16 JavaScriptでは仕様として文字列データの内部表現をUTF-16と定めている. このようなコードを実行した際,メモリ上に保存されるデータはCode Pointがそのまま保存 されるわけではなく,それをUTF-16で符号化した「0xD8, 0x42, 0xDF, 0x9F」が保存され る. →
つまり1要素2byteの配列で管理される
.lengthの挙動 .lengthはこの2byte配列の要素数を数えているだけ 4byteで表現される「𠮟」があるため,11文字だけど要素数が12なので.lengthは12を返す.
解決策
StringのIteratorを使う Stringがネイティブに実装しているIteratorの処理はCode Point単位で処理される. IteratorベースのSpread演算子を使えばCode Point単位に文字列を分割できる!
よし!これで文字数のカウ ントはバッチリだ!
...本当に?
1Code Point = 1文字 ではない
異体字セレクタという存在 Unicodeには漢字や絵文字のバリエーションを表す異体字セレクタというものがある. 例えば「葛」と「葛󠄀」の違いや,「 」と「 」の違いなど. つまりCode Pointの数と直感的な文字数が一致しない場合がある. これらは基本となる文字にCode Pointを定義して,その文字の後に異体字セレクタという別 のCode
Pointをつけることによって表現される.
結合文字という存在 意味的にはこの2つは等しいため,検索などでこの2つを異なる扱いをしてしまうと,直感に 反する可能性がある. Unicodeには結合文字という,複数のCode Pointを使って1文字を表現することがある. 例えば「が」は,「U+304C」と「U+304B, U+3099」の2通りの表し方がある.
絵文字の合字 合字とは「f」を2回重ねたときに「ff」のように2つがくっついた状態で表示されること. 一部の絵文字はこの合字を利用して複雑な絵文字を表現している場合がある. それぞれの絵文字の間にZero Point Joiner(U+200D)という不可視の文字を入れることに よってその絵文字は合成されているということを表す. ※Zero Point Joinerを使わないで合成される場合もあり(一部の国旗の絵文字など)
こんなのいちいち判別するプ ログラムなんて書けるか!
書けます
書記素クラスタと JSの便利なAPI
自然な区切りを表す書記素クラスタ これまで見てきたように,Code Point単位で見ても異体字セレクタや結合文字,合字などの 概念によってUnicodeにおける「1文字」というのはかなり表現するのが難しい. それに「1文字」という表現は非常に曖昧で,Byte単位なのか,Code Point単位なのか, 「いわゆる直感的な1文字」なのかが分かりづらい. そこで,「いわゆる直感的な1文字」をUnicodeでは書記素クラスタと呼んでいる. 書記素クラスタでの分割アルゴリズムはUnicodeの仕様として標準化されており,この仕様 に従えば誰でも直感的な1文字で文字列を分割することができる(とはいえアルゴリズムは非
常に複雑).
JSの便利なAPI,Intl.Segmenter() 書記素クラスタへの分割をJavaScriptでやる場合,ECMAScript標準APIである Intl.Segmenter()が使える. これを使うと文字列をロケールに応じて書記素,単語,文に分割することができる.
これでようやく正しく 「文字数」を 数えることができる
まとめ
まとめ n Unicodeにおける文字一つ一つに割り当てられるユニークなIDを Code Point と言g n JSでは文字列の内部表現がUTF-16で統一されていh n .lengthはCode
Point単位の処理ではないので,直感と異なる値を返す事があh n Code Point単位で得たい場合はIteratorを使g n Unicodeでは「1Code Point = 直感的な1文字」とは限らな6 n 異体字セレク n 結合文 n 絵文字の合 n et n 直感的な1文字のことをUnicodeでは 書記素クラスタ と呼Æ n JSでは Intl.Segmenter() を使うと簡単に書記素クラスタ単位に分割できる