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
230
文字数の話 〜Unicodeの楽しい話〜
プログラミングをする際に間違えがちな文字数のカウント方法について,Unicodeの仕組みを紐解きながら解説しています.
しゅん🌙
July 02, 2023
Tweet
Share
More Decks by しゅん🌙
See All by しゅん🌙
Rustで対戦型Tetrisを作った話
shunshobon
0
360
文字数の話の続き 〜Unicodeの楽しくない話〜
shunshobon
0
190
Haskellの並列・並行処理
shunshobon
1
350
Other Decks in Programming
See All in Programming
Agent Rules as Domain Parser
yodakeisuke
1
330
型付け力を強化するための Hoogle のすゝめ / Boosting Your Type Mastery with Hoogle
guvalif
1
230
CRUD から CQRS へ ~ 分離が可能にする柔軟性
tkawae
0
220
Devinで実践する!AIエージェントと協働する開発組織の作り方
masahiro_nishimi
6
2.5k
Reactive Thinking with Signals, Resource API, and httpResource @Devm.io Angular 20 Launch Party
manfredsteyer
PRO
0
130
ユーザーにサブドメインの ECサイトを提供したい (あるいは) 2026年函館で一番熱くなるかもしれない言語の話
uvb_76
0
170
インターフェース設計のコツとツボ
togishima
2
480
生成AI時代のフルスタック開発
kenn
10
2.7k
❄️ tmux-nixの実装を通して学ぶNixOSモジュール
momeemt
1
120
iOSアプリ開発もLLMで自動運転する
hiragram
6
2.1k
TypeScript エンジニアが Android 開発の世界に飛び込んだ話
yuisakamoto
6
950
TypeScript を活かしてデザインシステム MCP を作る / #tskaigi_after_night
izumin5210
4
470
Featured
See All Featured
The Cult of Friendly URLs
andyhume
78
6.4k
What's in a price? How to price your products and services
michaelherold
245
12k
Site-Speed That Sticks
csswizardry
7
590
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
32
5.8k
Art, The Web, and Tiny UX
lynnandtonic
298
21k
Faster Mobile Websites
deanohume
307
31k
4 Signs Your Business is Dying
shpigford
183
22k
Making Projects Easy
brettharned
116
6.2k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.9k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
5
620
Fireside Chat
paigeccino
37
3.5k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
6
650
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() を使うと簡単に書記素クラスタ単位に分割できる