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 05, 2023
Technology
0
160
文字数の話の続き 〜Unicodeの楽しくない話〜
前回の「文字数の話」に続き,実際に業務でアプリケーションを構築する際に起こりそうなUnicodeの問題について解説しています.
しゅん🌙
July 05, 2023
Tweet
Share
More Decks by しゅん🌙
See All by しゅん🌙
Rustで対戦型Tetrisを作った話
shunshobon
0
340
文字数の話 〜Unicodeの楽しい話〜
shunshobon
0
220
Haskellの並列・並行処理
shunshobon
1
330
Other Decks in Technology
See All in Technology
Multitenant 23ai の全貌 - 機能・設計・実装・運用からマイクロサービスまで
oracle4engineer
PRO
2
110
Riverpod & Riverpod Generatorを利用して状態管理部分の処理を書き換えてみる簡単な事例紹介
fumiyasac0921
0
100
Symfony in 2025: Scaling to 0
fabpot
2
170
非エンジニアにも伝えるメールセキュリティ / Email security for non-engineers
ykanoh
13
3.9k
Cloud Native PG 使ってみて気づいたことと最新機能の紹介 - 第52回PostgreSQLアンカンファレンス
seinoyu
1
180
React Server Componentは 何を解決し何を解決しないのか / What do React Server Components solve, and what do they not solve?
kaminashi
6
1.2k
[CATS]Amazon Bedrock GenUハンズオン座学資料 #2 GenU環境でRAGを体験してみよう
tsukuboshi
0
140
職種に名前が付く、ということ/The fact that a job title has a name
bitkey
1
240
モンテカルロ木探索のパフォーマンスを予測する Kaggleコンペ解説 〜生成AIによる未知のゲーム生成〜
rist
4
1.1k
Go製のマイグレーションツールの git-schemalex の紹介と運用方法
shinnosuke_kishida
1
400
Amazon GuardDuty Malware Protection for Amazon S3を使おう
ryder472
2
100
バックエンドエンジニアによるフロントエンドテスト拡充の具体的手法
kinosuke01
1
670
Featured
See All Featured
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
118
51k
Fontdeck: Realign not Redesign
paulrobertlloyd
83
5.4k
Making Projects Easy
brettharned
116
6.1k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.4k
Why You Should Never Use an ORM
jnunemaker
PRO
55
9.3k
Done Done
chrislema
183
16k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
28
2k
Into the Great Unknown - MozCon
thekraken
36
1.7k
Writing Fast Ruby
sferik
628
61k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
25k
A better future with KSS
kneath
238
17k
Transcript
文字数の話の続き Unicodeの楽しい話
自己紹介 t 名前: しゅん t Twitter: @shun_shobon / GitHub: @shun-shobo
t 学校: 長野高専 電子情報工学科 5F t 得意: Web Frontend / Web Frontend Ops / A11y なE t 研究: ホログラフィ・ヒューマンインタフェースなE t 趣味: PCゲーム・自作キーボーd t 一言: コンピュータと人との関わり方を模索しています
Kloud L Tで話したUnicode の話の続きになります
前回のあらすじ
Unicodeの目的 Unicodeは全ての文字にユニークなIDを降ること目的としている. このユニークなIDのことをUnicodeでは Code Point(コードポイント) と呼ぶ. チ → U+30C1 ゃ
→ U+3083 𠮟 → U+20B9F ! → U+FF01
Code Pointの符号化方法 このCode Pointを相手に送る際には,特定の方式によってバイナリにすることで送信される. これにはいくつかの方式がある. j UTF-32 … どのCode Pointも4Byteで表r
j UTF-16 ... 小さなCode Pointは2Byte,大きいのは4Byty j UTF-8 ... なるべく小さなByteになるように1〜4Byteで表す ※ものすごい雑な解説なので詳しくは調べてください
JSの.lengthの挙動 JSの.lengthは文字列の内部表現であるUTF-16の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 Width Joiner(U+200D)という不可視の文字を入れることに よってその絵文字は合成されているということを表す. ※Zero Width Joinerを使わないで合成される場合もあり(一部の国旗の絵文字など)
自然な区切りを表す書記素クラスタ これまで見てきたように,Code Point単位で見ても異体字セレクタや結合文字,合字などの 概念によってUnicodeにおける「1文字」というのはかなり表現するのが難しい. それに「1文字」という表現は非常に曖昧で,Byte単位なのか,Code Point単位なのか, 「いわゆる直感的な1文字」なのかが分かりづらい. そこで,「いわゆる直感的な1文字」をUnicodeでは書記素クラスタと呼んでいる. 書記素クラスタでの分割アルゴリズムはUnicodeの仕様として標準化されており,この仕様 に従えば誰でも直感的な1文字で文字列を分割することができる(とはいえアルゴリズムは非
常に複雑).
今回はこれらの知識を踏ま えて,実際のアプリ開発で 起こりそうな問題について 解説します
アプリ開発で起きる 問題色々
文字数制限が直感と異なる問題 入力フォームなどで文字数を制限してる場合,Code Point単位で文字数を数えていると直感 と異なる場合がある. 例えば「 」は7つのCode Point(U+1F3F4, U+E0067, U+E0062, U+E0077,
U+E006C, U+E0073, U+E007F)で表現されるが,これを20文字(20Code Point)で制限してる場合, 「 」はこの制限に引っかかることになる. ユーザー名などの場合,Code Pointよりも書記素クラスタで制限したほうが良いかもしれな い(もちろんちゃんと議論して合意を取ろう). 20文字で制限している → 「 」はアウト?
フォントが異なる問題 実は一部の絵文字は白黒の記号とCode Pointを共有している. 例えば「⁉」と「 」はどちらもU+2049である. どちらが表示されるかはフォントの優先順位などで決まる. きちんと指定する場合,Emoji Variation Selector(EVS)という異体字セレクタの一種を後ろ につけることで絵文字自体のバリエーションを選択できる.
フォントの優先順位やバリエーションをきちんとしないと,ユーザーが白黒の☎を打ったつ もりなのに全く別のものが表示されてしまうかもしれない. 「⁉」と「 」,どっちが表示されて欲しい?
絵文字の合字の環境依存問題 一部の絵文字の合字は環境依存の場合がある.例えばWhatsAppには「◯」5つをZWJで接続 することで作られた五輪のマークの絵文字があった(現在は削除済みの模様). 使用している端末,フォント,アプリケーションによって合字の絵文字はうまく表示されな い場合がある.ユーザーは特定の絵文字を入力したつもりでも,アプリ側ではうまく表示さ れない可能性もある. UnicodeではRGI(Recommended for General Interchange)と呼ばれる多プラットホームで
サポートされている絵文字を定義しているため,これに沿わない絵文字を弾くといった実装 をする必要が出てくるかもしれない. WhatsApp → それ以外 → ◯◯◯◯◯
結合文字列と 検索で生じる問題
合成文字列で生じる問題 「が(U+304C)」と「が(U+304B, U+3099)」はCode Point上の表記は違うが意味的にも視覚 的にも完全に同一である.そのため,検索等でこの2つを同一視しないと直感と異なる振る舞 いをすることになる. 普段合成文字列なんて使わねーよと思う人もいると思うが,実はmacOSのFinder上でファイ ルの作成や名前の変更をした場合,合成文字列に自動変換されている. 「が」と「が」は意味的・視覚的に等しい
合成文字列で生じる問題 どちらも同じ意味の文字列なのに,合成文字列の方はうまくNGチェックに引っかからなく なってしまう.
解決策
Unicode正規化
Unicode正規化 Unicode正規化は意味的に等しい文字列(文字)を統一させる処理のこと. 例えば全て合成済文字にしたり,全て結合文字列にしたりなど. NFC → 全て合成済文字にする(分解してから合成する) NFD → 全て結合文字列にする NFKC
→ NFCと似ているが,互換性のある文字も正規化する NFKD → NFDと似ているが,互換性のある文字も正規化する
Unicode正規化の例 JavaScriptではString.prototype.normalize()で正規化が可能. NFCで処理すると「が(U+304B, U+3099)」を「が(U+304C)」にすることができる.
Unicode正規化の例 NFKC/NFKDで処理すると視覚的に異なっても,意味的に同じならば正規化される(互換等価 という,逆にNFC/NFDは正準等価という). 検索等の実装では便利だが,視覚的な表現が変わってしまう場合があるので気をつける必要 がある(下の例以外にも,半角・全角が変わったりする).
Unicode正規化とやらを すれば良いんだな!
...本当に?
Unicode正規化に おける問題点
CJK互換漢字という存在 UnicodeではCJK(中国・日本・韓国)で使われる漢字を統一的に扱うCJK統合漢字という物が ある.これはUnicodeに漢字を収録する際に,「ほぼ同じ」漢字をCJK間で統合し,同一の Code Pointを振るために作られた. 一方,Unicodeに収録する際に出典元となった各国の標準規格との互換性を保つために,一 部の漢字はCJK統合漢字とは別に単体のCode Pointが割り当てられている. これをCJK互換漢字という. CJK統合漢字
→ 「羽」(U+7FBD) CJK互換漢字 → 「羽」(U+FA1E)
正準等価でも見た目が変わってしまう このCJK互換文字は,CJK統合漢字と視覚的に異なるのにも関わらず,Unicode正規化にお いて正準等価であるため,NFC/NFDで見た目が置き換わってしまうという問題がある. つまり検索等の都合で闇雲に正規化してしまうと一部の漢字の見た目が変わる可能性があ る. 「羽」(U+FA1E) ↓ NFC/NFDで正規化 ↓ 「羽」(U+7FBD)
解決策
異体字セレクタを使う 実はCJK互換文字は異体字セレクタを使用した2Code Pointで表現が可能(例外あるかも). 事前にCJK互換文字を異体字セレクタを使用した表現に変換しておけば,Unicode正規化に 巻き込まれずに済む. 「羽」(U+FA1E) ↓ 異体字セレクタを使った表現に変換 ↓ 「羽︀」(U+7FBD,
U+FE00) ↓ Unicode正規化 ↓ 「羽︀」(U+7FBD, U+FE00)
まとめ
Unicodeムズすぎ!