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
240
文字数の話 〜Unicodeの楽しい話〜
プログラミングをする際に間違えがちな文字数のカウント方法について,Unicodeの仕組みを紐解きながら解説しています.
しゅん🌙
July 02, 2023
Tweet
Share
More Decks by しゅん🌙
See All by しゅん🌙
Rustで対戦型Tetrisを作った話
shunshobon
0
370
文字数の話の続き 〜Unicodeの楽しくない話〜
shunshobon
0
210
Haskellの並列・並行処理
shunshobon
1
370
Other Decks in Programming
See All in Programming
Hack Claude Code with Claude Code
choplin
7
2.5k
[SRE NEXT] 複雑なシステムにおけるUser Journey SLOの導入
yakenji
0
150
イベントストーミング図からコードへの変換手順 / Procedure for Converting Event Storming Diagrams to Code
nrslib
2
1.1k
顧客の画像データをテラバイト単位で配信する 画像サーバを WebP にした際に起こった課題と その対応策 ~継続的な取り組みを添えて~
takutakahashi
4
1.3k
Modern Angular with Signals and Signal Store:New Rules for Your Architecture @enterJS Advanced Angular Day 2025
manfredsteyer
PRO
0
270
AIと”コードの評価関数”を共有する / Share the "code evaluation function" with AI
euglena1215
1
180
Quand Symfony, ApiPlatform, OpenAI et LangChain s'allient pour exploiter vos PDF : de la théorie à la production…
ahmedbhs123
0
220
TypeScriptでDXを上げろ! Hono編
yusukebe
3
770
CDK引数設計道場100本ノック
badmintoncryer
2
480
AI駆動のマルチエージェントによる業務フロー自動化の設計と実践
h_okkah
0
230
たった 1 枚の PHP ファイルで実装する MCP サーバ / MCP Server with Vanilla PHP
okashoi
1
300
Google Agent Development Kit でLINE Botを作ってみた
ymd65536
2
260
Featured
See All Featured
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
48
2.9k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
35
2.4k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
29
9.6k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
8
700
StorybookのUI Testing Handbookを読んだ
zakiyama
30
5.9k
Faster Mobile Websites
deanohume
308
31k
What's in a price? How to price your products and services
michaelherold
246
12k
We Have a Design System, Now What?
morganepeng
53
7.7k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
138
34k
Fireside Chat
paigeccino
37
3.5k
Making Projects Easy
brettharned
116
6.3k
Measuring & Analyzing Core Web Vitals
bluesmoon
7
520
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() を使うと簡単に書記素クラスタ単位に分割できる