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
130
文字数の話の続き 〜Unicodeの楽しくない話〜
前回の「文字数の話」に続き,実際に業務でアプリケーションを構築する際に起こりそうなUnicodeの問題について解説しています.
しゅん🌙
July 05, 2023
Tweet
Share
More Decks by しゅん🌙
See All by しゅん🌙
Rustで対戦型Tetrisを作った話
shunshobon
0
300
文字数の話 〜Unicodeの楽しい話〜
shunshobon
0
210
Haskellの並列・並行処理
shunshobon
1
290
Other Decks in Technology
See All in Technology
三菱電機で社内コミュニティを立ち上げた話
kurebayashi
1
360
30分でわかる「リスクから学ぶKubernetesコンテナセキュリティ」/30min-k8s-container-sec
mochizuki875
3
450
comilioとCloudflare、そして未来へと向けて
oliver_diary
6
450
今から、 今だからこそ始める Terraform で Azure 管理 / Managing Azure with Terraform: The Perfect Time to Start
nnstt1
0
240
EMConf JP の楽しみ方 / How to enjoy EMConf JP
pauli
2
150
20250116_自部署内でAmazon Nova体験会をやってみた話
riz3f7
1
100
AWSサービスアップデート 2024/12 Part3
nrinetcom
PRO
0
140
Oracle Base Database Service 技術詳細
oracle4engineer
PRO
6
54k
Azureの開発で辛いところ
re3turn
0
240
2024年活動報告会(人材育成推進WG・ビジネスサブWG) / 20250114-OIDF-J-EduWG-BizSWG
oidfj
0
230
2025年に挑戦したいこと
molmolken
0
160
AWS re:Invent 2024 re:Cap Taipei (for Developer): New Launches that facilitate Developer Workflow and Continuous Innovation
dwchiang
0
170
Featured
See All Featured
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
Making Projects Easy
brettharned
116
6k
Raft: Consensus for Rubyists
vanstee
137
6.7k
How GitHub (no longer) Works
holman
312
140k
The Art of Programming - Codeland 2020
erikaheidi
53
13k
The Cult of Friendly URLs
andyhume
78
6.1k
Embracing the Ebb and Flow
colly
84
4.5k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
19
2.3k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
33
2.7k
Bash Introduction
62gerente
610
210k
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ムズすぎ!