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
120
文字数の話の続き 〜Unicodeの楽しくない話〜
前回の「文字数の話」に続き,実際に業務でアプリケーションを構築する際に起こりそうなUnicodeの問題について解説しています.
しゅん🌙
July 05, 2023
Tweet
Share
More Decks by しゅん🌙
See All by しゅん🌙
Rustで対戦型Tetrisを作った話
shunshobon
0
250
文字数の話 〜Unicodeの楽しい話〜
shunshobon
0
200
Haskellの並列・並行処理
shunshobon
1
270
Other Decks in Technology
See All in Technology
ライブラリでしかお目にかかれない珍しい実装
mikanichinose
2
330
形式手法の 10 メートル手前 #kernelvm / Kernel VM Study Hokuriku Part 7
ytaka23
5
750
SREの組織類型に応じた リーダシップの考察
kenta_hi
PRO
1
620
Terraform未経験の御様に対してどの ように導⼊を進めていったか
tkikuchi
1
250
OCI Data Integration技術情報 / ocidi_technical_jp
oracle4engineer
PRO
1
2.6k
dev 補講: プロダクトセキュリティ / Product security overview
wa6sn
0
1.6k
福岡新卒エンジニアの会
teba_eleven
1
190
ジョブマッチングサービスにおける相互推薦システムの応用事例と課題
hakubishin3
3
620
データ活用促進のためのデータ分析基盤の進化
takumakouno
2
420
TinyGoを使ったVSCode拡張機能実装
askua
2
200
株式会社島津製作所_研究開発(集団協業と知的生産)の現場を支える、OSS知識基盤システムの導入
akahane92
1
180
Lambdaと地方とコミュニティ
miu_crescent
2
230
Featured
See All Featured
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Visualization
eitanlees
145
15k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
42
2.2k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
42
9.2k
Done Done
chrislema
181
16k
Java REST API Framework Comparison - PWX 2021
mraible
PRO
28
8.2k
The Art of Programming - Codeland 2020
erikaheidi
52
13k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5k
Become a Pro
speakerdeck
PRO
25
5k
Practical Orchestrator
shlominoach
186
10k
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ムズすぎ!