Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
文字数の話の続き 〜Unicodeの楽しくない話〜
Search
しゅん🌙
July 05, 2023
Technology
0
130
文字数の話の続き 〜Unicodeの楽しくない話〜
前回の「文字数の話」に続き,実際に業務でアプリケーションを構築する際に起こりそうなUnicodeの問題について解説しています.
しゅん🌙
July 05, 2023
Tweet
Share
More Decks by しゅん🌙
See All by しゅん🌙
Rustで対戦型Tetrisを作った話
shunshobon
0
280
文字数の話 〜Unicodeの楽しい話〜
shunshobon
0
210
Haskellの並列・並行処理
shunshobon
1
280
Other Decks in Technology
See All in Technology
知らない景色を見に行こう チャンスを掴んだら道が開けたマネジメントの旅 / Into the unknown~My management journey~
kakehashi
10
1.1k
検証と資産化を形にするプロダクト組織へ/tapple_pmconf2024
corin8823
1
9k
ONNX推論クレートの比較と実装奮闘記
emergent
0
290
.NET のUnified AI Building Blocks 入門...!
okazuki
0
190
Oracle Cloud Infrastructure:2024年11月度サービス・アップデート
oracle4engineer
PRO
0
230
2024/11/29_失敗談から学ぶ! エンジニア向けre:Invent攻略アンチパターン集
hiashisan
0
430
LINEヤフーにおける超大規模プラットフォーム実現への挑戦と学び / Challenges and Lessons in Building an Ultra-Large-Scale Platform at LY Corporation
hhiroshell
3
1.1k
店舗向けSaaSにおける 顧客要望活用の実践アプローチ(20241205_pmconf)
yujirooo
0
3k
12/2(月)のBedrockアプデ速報(re:Invent 2024 Daily re:Cap #1 with AWS Heroes)
minorun365
PRO
2
310
How is Cilium Tested?
yutarohayakawa
5
290
Autonomous Database サービス・アップデート (FY25)
oracle4engineer
PRO
0
260
【AWS re:Invent 2024】Amazon Bedrock アップデート総まとめ
minorun365
PRO
7
500
Featured
See All Featured
RailsConf 2023
tenderlove
29
920
We Have a Design System, Now What?
morganepeng
51
7.3k
KATA
mclloyd
29
14k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
Thoughts on Productivity
jonyablonski
67
4.3k
Site-Speed That Sticks
csswizardry
1
140
ReactJS: Keep Simple. Everything can be a component!
pedronauck
665
120k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
How to Ace a Technical Interview
jacobian
276
23k
Six Lessons from altMBA
skipperchong
27
3.5k
Practical Orchestrator
shlominoach
186
10k
Become a Pro
speakerdeck
PRO
25
5k
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ムズすぎ!