Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Unicode文字列処理

https://edge.connpass.com/event/161663/ にて登壇。

今現在、Unicodeという文字規格には13万個以上の文字が収録されています。それぞれの文字には文字のカテゴリー、文字と文字の連結方法、左右どちらから読むかなど、様々な付帯情報も定められています。英語でドキュメントがあり、例えばアラビア語を読めなくてもアラビア文字のレンダリング処理を書ける程度には詳細な仕様が書かれています。
本セッションでは、このUnicodeの仕様の概要と、それをプログラム(主にUnity上でのC#を想定)的に処理する際の注意点などについて説明します。

Related Books

Free with a 30 day trial from Scribd

See all
  • Be the first to comment

Unicode文字列処理

  1. 1. 文字列処理 岩永 信之
  2. 2. 自己紹介 • 普段はスマホゲーム作ってます(常時求人中) ジャンル : RPG 公式Twitter : https://twitter.com/majo_meikyu ジャンル : ストラテジー 共同開発 : 株式会社gumi 公式サイト : http://www.cryuni.com/ http://orange-cube.jp/ C#な会社です Docker, k8s, .NET Core 3.1
  3. 3. 今日の話 「文字」の話をします。
  4. 4. 時代背景など • 多くが西欧の国 • ほっとくとラテン文字ばかり優遇 • 普及期の非ラテン文字 ≒ 日本 • 非ラテン文字の中ではかな漢字は だいぶこなれてる方 • 日本がやらかした黒歴史がそのま まUnicodeに残ってたりも • 商機のわりに未熟な文字あり • アラブ(石油王)とか • インド(GDP伸び盛り)とか GDP推移※ ※ http://www.garbagenews.net/archives/1335765.html Windows 95 (PCの一般家庭普及) Unicode 1.0制定 絵文字
  5. 5. 文字? • ここでいう文字とは? • letter … 「a」とか「あ」とか音に出して読める文字 • character … 記号類、制御文字なども含む • 文字コードの「文字」はcharacter あんまり文字っぽくない「character」の例※: 0000 … ヌル文字 (「どの文字でもない」を表す。大体は文字列末尾を検出するための番兵) 00D0 … 改行 0301 … acuteアクセント「 ́ 」 (1文字前のletterの上に乗っかる) 8205 … zero width joiner (前後の文字を分割しないようにする) 8207 … right to left mark (この文字以降を右書きにする) ※ 文字がらみで16進数が出てきたら符号点(文字に割り当てられた数字)のことだと思ってください
  6. 6. 改めて今日の話 • コンピューター内で文字(character)がどう扱われているか • Unicode前提 • まず、Unicodeの話から • いろいろとある文字の面倒事 • ラテン文字しか受け付けないプログラムが結構ある理由 • 日本語を受け付けるプログラムでも絵文字が怪しいことがある理由 • C#で何も考えずにstringを使うと遅いことが結構ある理由 • 絵文字が踏み抜いちゃった地雷 • 日本語とかまだ楽な部類という話(アラビア文字を例に)
  7. 7. 改めて今日の話 • コンピューター内で文字(character)がどう扱われているか • Unicode前提 • まず、Unicodeの話から • いろいろとある文字の面倒事 • ラテン文字しか受け付けないプログラムが結構ある理由 • 日本語を受け付けるプログラムでも絵文字が怪しいことがある理由 • C#で何も考えずにstringを使うと遅いことが結構ある理由 • 絵文字が踏み抜いちゃった地雷 • 日本語とかまだ楽な部類という話(アラビア文字を例に) • 文字は国・文化に直結してるのでうかつなことすると 「よろしい、ならば戦争だ」 • 今日持ち帰っていただく感想はたぶん 「面白いけど関わりたくない」
  8. 8. 文字コード 符号点と符号化方式
  9. 9. 符号点と符号化方式 • 符号点 (code point) • どの文字にどの番号を振るか※ • 文字符号化形式 (character encoding) • その番号をどうやって記録するか a … 61 あ … 3042 😊 … 1F60A ※ 本稿では常に16進数で表記 符号点 UTF-8 UTF-16 UTF-32 61 61 0061 00000061 3042 E3 81 82 3042 00003042 1F60A F0 9F 98 8A D83D DE0A 0001F60A 例: 例: この意味では今時「Unicode一択」 (レガシー資産がなければ他の文字 コードのことは考えなくていい) この意味では同じUnicodeでも 複数の符号化形式がある
  10. 10. 符号点 • 符号点 (code point) • どの文字にどの番号を振るか • 仕様上は16進数で0~10FFFFまでの約111万文字 • 実際に使われているのは137,928文字 (Unicode 12.0時点) a … 61 あ … 3042 😊 … 1F60A 例:
  11. 11. 符号化方式 • Unicodeでは大きく分けて3種類 • UTF-8 : 符号点あたり1~4バイト(8~32ビット)の可変長 • UTF-16 : 符号点あたり2か4バイトの可変長 (ただし、大半の文字が2バイト(16ビット)) • UTF-32 : 4バイト(32ビット)固定 • 細かくはエンディアンによってさらに分かれる (Big/Little) • UTF-16 BE, UTF-16 LE, UTF-32 BE, UTF-32 LE 符号点 UTF-8 UTF-16 UTF-32 61 61 0061 00000061 3042 E3 81 82 3042 00003042 1F60A F0 9F 98 8A D83D DE0A 0001F60A 30 42 / 42 30 00 01 F6 0A / 0A F6 01 00 例:
  12. 12. Unicodeの符号化方式 • Unicodeでは大きく分けて3種類 • UTF-8 : 符号点あたり1~4バイト(8~32ビット)の可変長 • UTF-16 : 符号点あたり2か4バイトの可変長 (ただし、大半の文字が2バイト(16ビット)) • UTF-32 : 4バイト(32ビット)固定 • この辺りに「ラテン文字しか受け付けない」、 「絵文字の処理がおかしい」の原因あり • どうしてこうなったかはちょっと歴史の話が必要
  13. 13. ASCIIコード 原初の7ビット文字コード
  14. 14. ASCIIコード • 1963年にアメリカで規格化された文字コード • 7ビット(128文字) 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI 1 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 2 SP ! " # $ % & ' ( ) * + , - . / 3 0 1 2 3 4 5 6 7 8 9 : ; < = > ? 4 @ A B C D E F G H I J K L M N O 5 P Q R S T U V W X Y Z [ ⧵ ] ^ _ 6 ` a b c d e f g h i j k l m n o 7 p q r s t u v w x y z { | } ~ DEL ASCII = American Standard Code for Information Interchange の略
  15. 15. ASCIIコード • 1963年にアメリカで規格化された文字コード • コンピューターの大量生産が始まった時期・場所 = 世界標準規格の元になってる • 今でもASCII • ASCIIしか受け付けないプロトコルが普通に現存 • インターネットで流通している文字の大部分がASCII • 非ラテン文字の文字コードも「ASCII互換」が重要視される • 最初の128文字がASCIIと同じ • ASCIIバイト列を無変換で受け付ける
  16. 16. インターネット上の大部分がASCII • 例えばHTTP 1.1ヘッダー HTTP/1.1 200 OK Date: Mon, 27 Jan 2020 03:45:20 GMT Vary: Accept-Encoding,Cookie,Authorization Server: ATS/8.0.5 X-ATS-Timestamp: 1580096720 Content-Type: text/html; charset=UTF-8 X-Powered-By: PHP/7.2.26-1+0~20191218.33+debian9~1.gbpb5a340+wmf1 X-Content-Type-Options: nosniff P3P: CP="See https://ja.wikipedia.org/wiki/Special:CentralAutoLogin/P3P for more info." Content-language: ja Content-Encoding: gzip Last-Modified: Sun, 19 Jan 2020 07:05:51 GMT Backend-Timing: D=196662 t=1579418807535784 X-Varnish: 152932621 767971738 Age: 28457 X-Cache: cp5011 miss, cp5012 hit/40 X-Cache-Status: hit-front Server-Timing: cache;desc="hit-front" Strict-Transport-Security: max-age=106384710; includeSubDomains; preload X-Client-IP: 118.238.249.198 Cache-Control: private, s-maxage=0, max-age=0, must-revalidate Accept-Ranges: bytes Content-Length: 168955 Connection: keep-alive 何語のページを見ようと、先頭には必 ずこんな感じのヘッダーが入ってる (この部分は全部ASCII)
  17. 17. インターネット上の大部分がASCII • 例えばHTML(表示上) 我、15億人の文字ぞ? ラテン文字ごときがそんな?
  18. 18. インターネット上の大部分がASCII • 例えばHTML(ソースコード) この辺り全部ASCII 表示上はあんなに漢字だらけなのに… ソースコード上は ASCII : 42万文字 非ASCII : 3万文字
  19. 19. インターネット上の大部分がASCII • 例えばソースコード • キーワードとかは全部ASCII • 変数名に非ASCII文字を使う人は極めて少数 • 使ってよくてもIMEがめんどくさい • 非ASCII文字はほぼ文字列リテラル中のみ using System; var line = Console.ReadLine(); var x = int.Parse(line); $"{x} × {x} = {x * x}" この例で言うと非ASCII文字は ×(乗算記号、00D7)のみ • 昔のコンパイラーはリテラル・コメント以外 の非ASCII文字を受け付けないことも多かった • 最近のコンパイラーも、まずASCII文字だけを 処理するFast Pathがあったり
  20. 20. ASCII互換大事(再) • 流通の大部分を占めるASCII文字列の処理は重要 • ASCIIを無変換で扱いたい • UTF-8 (後述)が主流になったのもこれが理由 • C#とかJavaのcharはUTF-16 (ASCIIから変換が必要) • 文字列処理が遅い原因に
  21. 21. Unicode 世界中の文字を1つの文字コード体系に
  22. 22. Unicodeの符号点(一例) • 0~8F: ラテン文字。ASCII互換 • 10~FF: áとか、西欧で使う装飾付きラテン文字(通称Latin-1) • 250~2AF: 国際音声記号 • 370~3FF: ギリシャ文字 Ελληνικά • 400~52F: キリル文字 Кириллица • 600~6FF: アラビア文字 ‫اللغة‬ • 3040~30FF: ひらがな・カタカナ • 4E00~9FFF: 漢字 当初、2万文字あれば足りると思ってた Unicodeも2バイト固定を目指してた
  23. 23. 2バイト(6万5千文字)では足りない • 世界中の文字を全部入れたい • マイナーな漢字も入れようとすると4万文字くらい増えた • 古代文字も全部入れたいとか言い出すとどんどん増える • ということで追加 • 0~FFFF: 基本多言語面(前項のラテン文字~基本漢字) • 10000~1FFFF: 追加多言語面(古代文字や記号、絵文字) • 20000~2FFFF: 追加漢字面 • 30000~3FFFF: 第三漢字面(古代の漢字とか甲骨文字) • E0000~EFFFF: 追加特殊用途面(絵文字装飾とか用の特殊文字) 例えばヒエログリフ(𓄿𓄿𓄿)とか楔形文字( )とか
  24. 24. Unicodeの符号化方式 • UTF-8 • ASCII互換で、1~4バイトの可変長 • ASCIIだけ1バイト、かな漢字は3バイト • UTF-16 • Unicode、当初は2バイト固定長のつもりだった • 基本多言語面(0~FFFF)の文字は符号点そのまま • 追加面の文字をD800~DBFFとDC00~DFFFの範囲の2文字ペアで表現 • UTF-32 • 符号点そのまま4バイト固定長で記録 • 13万文字しか使ってないのに常に4バイト必要 さすがに無駄 UTF-8とUTF-16だけ説明 UTF = Unicode Transform Format
  25. 25. UTF-8 • 1~4バイトの可変長符号化 • 2バイト: ギリシャ文字、キリル文字、アラビア文字など • 3バイト: 日中韓(かな漢字、ハングル)など • 4バイト: 一部の漢字、絵文字など ビット数 最小値 最大値 変換方法 ビット パターン 元コード ポイント 1バイト目 2バイト目 3バイト目 4バイト目 7 0000 007F そのまま xxxxxxx 0xxxxxxx 11 0080 07FF 2バイト化 xxxxxxxxxxx 110xxxxx 10xxxxxx 16 0800 FFFF 3バイト化 xxxxxxxxxxxxxxxx 1110xxxx 10xxxxxx 10xxxxxx 21 10000 1FFFF 4バイト化 xxxxxxxxxxxxxxxxxxxxx 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ASCIIはASCIIのまま 2バイト目以降に1バイト目 と被る値が出てこない
  26. 26. UTF-16 • 2 or 4バイトで符号化 • 2バイト: ASCII、日中韓含めて大半の文字 • ASCIIにとっては不利 • 日中韓にとっては有利 • 4バイト: 一部の漢字、絵文字など • 4バイトになる条件はUTF-8と同じ 範囲 変換 方法 ビット パターン 元コード ポイント 1文字目 2文字目 0000~D7FF, E000~FFFF そのまま xxxxxxxxxxxxxx xxxxxxxxxxxxxx 10000以上 2文字使う uuuuuxxxxxxxxxxxxxxxx 110110wwwwxxxxxx 110111xxxxxxxxxx ※ wwww = uuuuu - 1 ※ • 元々2バイト固定のつもりでいた ところに無理やりねじ込んだ仕様 • 「2文字で2符号点を表す」的に解 釈される(surrogate pair (代用対)) これのせいでD800~DFFFの範囲を Unicode符号点として使えなくなった
  27. 27. UTF-16のASCII非互換 • 値としてはASCII互換だけど、2バイトに膨らます処理が必要 • 2倍の長さの配列を確保して • 値をコピーして… abcáèö 61 62 63 E1 E8 F6 0061 62 63 E1 E8 F600 00 00 00 00 Latin-1 UTF-16 案外高コスト
  28. 28. 4バイト文字(追加面文字) • 10000以上の文字(UTF-8でもUTF-16でも4バイト)は… • レア(だった) • 絵文字普及前は本当にめったに使われなかった • 後付け仕様 • 元々は2バイト固定のつもりだった • 後付けなので「追加面」(supplementary plains)と呼ばれる • よくバグってた • UTF-16で代用対を受け付けないコードは今でも多い • UTF-8を「最大3バイトの符号化方式」と思って書かれてるコードあり • 「絵文字のおかげで欧米人が4バイト文字に対応してくれた」 Unityとかまさに MySQLが5.4までは そうだったらしい
  29. 29. 流れの変化 • 2000年代半ばを境に流れが変わった • 2バイト固定長にできないのならUTF-16のメリットはあるのか • ASCII互換性の方が大事ではないか(実際大事) • プログラミング言語の文字列の内部形式の変遷: • C#、Java、JavaScriptとかはUTF-16 • Go、RustとかはUTF-8 これのせいで文字列処理が遅くなったりする※ ※ C#は最近(.NET Core 3.0で) Utf8クラスを追加してたりする (stringを介さず文字列処理することでパフォーマンス改善)
  30. 30. 参考: C#でUTF-8処理 • .NET CoreにはUTF-8化の波が来てる • .NET Core 3.0 (去年)で Utf8クラスを追加 • .NET 5 (今年末予定)でstringに変わるUtf8Stringクラスを追加予定 • たぶんUnityには当面来ないけど… • 自前実装してでもUTF-8処理するとかなり高速化する • 実際自分もやったことあり • https://github.com/ufcpp/Utf8Utils • https://github.com/ufcpp/UnicodeDataCsharp • 最速JSONシリアライザーが速いのはその辺りにも理由あり
  31. 31. UTF-16の「ほとんどの文字が固定長」 • 一応、UTF-16は「ほとんどの文字が固定長」 • 絵文字の登場頻度もそう高くはない • 4バイトな漢字は極めてレア • 「ほぼ」固定長にはそれなりのメリットあり • 頻度に偏りがある分岐は速い(分岐予測が当たる) • 「何個か先の文字をのぞき見しつつ」みたいな処理を繰り返すなら、 UTF-8よりUTF-16の状態の方が速い
  32. 32. 代用対の片割れ (incomplete surrogate) • 代用対の片割れは文字として認められるか • 例: • D830が単体で出てきた場合をどうみなすべきか • おおらかだったころ • Unicodeは2バイト固定の文字コードのつもりだった • 「2文字で1文字を代用」と考える • D830は「代用対の片割れ」(の内の上位(high surrogate))の「文字」 符号点 1F600 UTF-8 UTF-16 F0, 9F, 98, 80 D83D, DE00
  33. 33. 代用対の片割れ (incomplete surrogate) • 代用対の片割れは文字として認められるか • 例: • D830が単体で出てきた場合をどうみなすべきか • 今の考え方 • D830はあくまでUTF-16符号化の結果として現れるだけ • 有効な符号点ではない • 不正な値はFFFD (replacement character �)に置換すべき 符号点 1F600 UTF-8 UTF-16 F0, 9F, 98, 80 D83D, DE00 セキュリティ リスク回避
  34. 34. Unicode Scalar、WTF-8 • 「有効な符号点」だけを指す用語が必要になった • 符号点から代用対用のD800~DFFFを除外 • 互換性用に「不正なUTF-8」が必要になることがある • D830をUTF-8符号化するとき 推奨方式 : FFFD に置き換えた上で符号化して EF, BF, BD 互換モード : そのまま疑似的に符号化して ED, A0, BD Unicode Scalarと呼ぶ WTF-8 (wobbly transformed format)とかいう仕様があったり (wobbly: グラグラ、不安定)
  35. 35. 書記素 符号点 ≠ 人が「文字」だと認識する単位 どの道「文字」は可変長
  36. 36. 符号点/UTF-32なら「固定長」? • 否: Character同士が結合して1文字をなす a (ラテン文字) ́ (アクセント記号) á か (ひらがな) ゙ (濁点) が 接合文字 (絵文字) (絵文字) ᄋ (ハングル字母) ᅡ (ハングル字母) 아 U+61 U+301 U+304B U+3099 U+110B U+1161 U+1F469 U+1F469U+200D 結合文字 (タイ語子音) (タイ語母音) U+0E2D U+0E31 字母の組み合わせ 㐂 (漢字) 異字体 セレクター 㐂󠄁 U+3402 U+E0101 㐂 (漢字) 㐂󠄂 U+3402 U+E0101 (絵文字) U+1F469 異字体 セレクター (肌トーン) U+1F3FB 異字体 絵文字 (地域指定) U+1F1EF J (地域指定) U+1F1F5 P (地域指定) U+1F1FA U (地域指定) U+1F1F8 S 例:
  37. 37. 結合文字 (combining mark) • 1文字前の文字にくっつく記号がある • ラテン文字の発音区別符号(diacritical mark)とか • 日本語の濁点、半濁点とか a (ラテン文字) ́ (アクセント記号) á U+61 U+301 か (ひらがな) ゛ (濁点) が U+304B U+3099 ちなみに、1文字で「á」 を表す符号点もある(E1) 同じく、「が」にも 符号点がある(304C)
  38. 38. 何のための結合文字か • マイナーな組み合わせのための結合文字 • か゚ … か + 半濁点 (304B + 309A) • 意識しないと区別がつかないけど、日本語の「が」には鼻濁音 (nga)が混ざる • 放送業界でgaとngaの区別のために半濁点を使ったことがあるらしい • せ゚… せ + 半濁点 (305B + 309A) • アイヌ語の「ちぇ」の音の表記に使われることがあった • 今は普通に「ちぇ」で表記 • ら゚… ら + 半濁点 (3089 + 309A) • 外来語の L 音と R 音の区別のために使ってた時期があるらしい • L 音に半濁点 こんなマイナー用途に 1つの符号点は与えない
  39. 39. 書記素 • 符号点 ≠ 人が1文字と認識する単位 • 人が1文字と認識する単位を書記素 (grapheme)と呼ぶ • (言語学方面でのgraphemeとは用法がちょっと違うみたいなので注意) • 書記素を構成する符号点の塊を書記素クラスター(grapheme cluster)と 呼ぶ • カーソル移動、選択、削除はこの単位で行うべき • さもないと家族が1人、また1人と削られていく… どう見ても1文字に しか見えないけど… ZWJ ZWJ ZWJ 11個の符号点の塊で構成されてる = 書記素クラスター
  40. 40. 書記素クラスター分割 • unicode.orgに仕様あり • UAX #29: Unicode Text Segmentation • 昔はシンプルだった • 「letter + combining mark」くらい • 今は結構複雑 • 東南アジアに面倒なくっつき方する文字が結構あった • 漢字の異字体選択(後述)とかが仕様に入った • 絵文字のせいでカオス(後述) とりあえず覚えておくべきことは 「文字コードというのはどうあがいても可変長」
  41. 41. いろんな文字の落とし穴 昔の文字コードの名残があったり いろんな言語にはいろんな背景があったり
  42. 42. 世界中の文字を収めるということは… • 世界中の問題をそのまま抱えることに • Unicodeは、いろんな言語のいろんな文字を処理するためのデータも いろいろ提供 • UCD (Unicode Character Database) • https://www.unicode.org/Public/UCD/latest/ucd/ • ここから先は、いくつかの言語を例に文字の落とし穴的な話を
  43. 43. Latin-1 拡張ラテン文字 西欧言語の大部分をカバー Unicode符号点のFF以下の部分
  44. 44. Latin-1 • ASCIIコードに加えて128文字 • 1バイトで西欧の大部分をカバー 0 1 2 3 4 5 6 7 8 9 A B C D E F 8 PAD HOP BPH NBH IND NEL SSA ESA HTS HTJ VTS PLD PLU RI SS2 SS3 9 DCS PU1 PU2 STS CCH MW SPA EPA SOS SGCI SCI CSI ST OSC PM APC A NBSP ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ SHY ® ¯ B ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ C À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï D Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß E à á â ã ä å æ ç è é ê ë ì í î ï F ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ
  45. 45. ASCIIの次に支配的な地位 • Unicode符号点の0~FFはLatin-1互換 • ただ、UTF-8に符号化すると80以降は互換性なし • 2バイト化する • UTF-8はASCII互換だけどLatin-1互換じゃない • 「西」の人はよく「Latin-1 で十分」とか思ってるので要注意 • 「1バイト文字といえばASCIIじゃなくてLatin-1」と思ってる節あり • 「文字コードが不明ならとりあえずLatin-1扱い」みたいなコードよくあった • 文字化けでÀÁÂÃÄÅとかをよく見るのはそのせい
  46. 46. Latin-1の時点で変な文字 • ß : 元々はssの合字だけど今はssと使い分けられる • 合字のわりにだいぶ字形が違う • Þ þ : これだけラテン起源じゃなくてルーン文字起源 • 多くの言語ではthに転記して消失。アイスランド語とかで現存 • ì í î ï: i+発音記号だけど i に元々あるドットが消失 • ÿ : フランス語だとy+発音記号、オランダ語だとijの合字
  47. 47. Latin-1にないラテン文字 • œ : oeの合字 • フランス語でまれに使う • ギリシャ語起源の単語にだけ登場 • 起源が違うのでoeとは読みが違う、読みが違うなら別の文字にしたい • æはLatin-1だけどœはLatin-1じゃない • ẞ : ßの大文字 • 語頭に立たない文字なので近代まで不要とされてた • 急に必要論が高まって2008年に Unicode にも採用 • ı :トルコ語では I の小文字はこれ • i の大文字は İ
  48. 48. 漢字 Unicodeが2バイトに収まらない最たる元凶 利用者は多く、こなれてはいる
  49. 49. Unicodeにおける漢字の境遇 • 「非ラテン文字の中ではかな漢字はだいぶこなれてる方」 • 利用者が多いから「バグ修正」が進んでるだけ • パッチにパッチを重ねてる状態なのでカオス度は高い 「できなくて困る」みたいなのは少ないけど 処理が簡単なわけじゃない
  50. 50. 同じ意味の別字体 • 日本語の漢字と中国語の漢字は同じか • 例えばヨーロッパの言語だと… • A (0041) : ラテン文字のア (エイ) • Α (0380) : ギリシャ文字のアルファ • А (0410) : キリル文字のアー • a (0061) : ラテン文字Aの小文字 • a (0061) : ↑と同じ文字。書体違い 同源、同字形、同系統の音だけど それぞれに符号点割り当てあり これだけ字形が違っても同じ文字 符号点も同じ
  51. 51. 結局どうなったか • Unicode制定時、二転三転したみたい • 字形が違っても同源、同意のものは統合すべきである • 同源、同意で、字体が近いものだけ統合しよう • というか、文字多過ぎてわからねぇ • 結局、全然わからない。雰囲気で決まっている※ ※ 当然、最大限努力をしたけど無理だった結果なので最初から雰囲気で決めたわけではない 日本 簡体字(中国本土) 繁体字(台湾・香港) 4E9C 4E9A 4E9E 別の符号点が割あたってる 6B21 6B21 6B21 同じ符号点 フォントで字体が違う
  52. 52. フォントを出し分けたいんだけど… • と は文字コード上は完全に同じ文字 • 例: https://ja.wikipedia.org/wiki/%E7%B9%81%E4%BD%93%E5%AD%97 表示結果: HTMLの内容: <td lang="zh-tw">次</td> <td lang="zh-hk">次</td> <td lang="zh-cn">次</td> <td lang="ko">次</td> <td>次</td> マークアップで言語なりフォントなり を明示的に指定しないと無理
  53. 53. 自動判定 • ある程度の自動判定はできなくもない • 「ひらがなを含んでいたら日本語」みたいな荒い判定 • 簡体字でしか使わない符号点、繁体字でしか使わない符号点もある • その手の情報も一応 UCD がデータ提供して…
  54. 54. 自動判定 • ある程度の自動判定はできなくもない • 「ひらがなを含んでいたら日本語」みたいな荒い判定 • 簡体字でしか使わない符号点、繁体字でしか使わない符号点もある • その手の情報も一応 UCD がデータ提供して…
  55. 55. 自動判定 • ある程度の自動判定はできなくもない • 「ひらがなを含んでいたら日本語」みたいな荒い判定 • 簡体字でしか使わない符号点、繁体字でしか使わない符号点もある • その手の情報も一応 UCD がデータ提供して… 展開後、合計34MB
  56. 56. 日本語だけでも… • 人名漢字は分けざるを得ない • 「紛らわしいからお前今日から斉藤な」とは言えない • 斉、斎、齋、齊、齋、齎 • 源流・意味で分けると2つだけ • 斉 … ひとしく。本来の読みは「せい」 • 斎 … ものいみ • 異字体 全部別の符号点 吉 5409 𠮷 20bb7 異字体に別の符号点が割あたってる (「つちよし」。吉野家の吉の本来の字体) 葛 845B 葛󠄀 845B + E0100 符号点は1つ 追加で字体選択用の制御文字を付ける (異字体セレクター (variation selector)) 当たり前のように追加面文字
  57. 57. 特に機械的なルールはない • 愚直にテーブルを持つしかない • さきほどの「34MB」から必要な部分を抜き出して使ったり • OSに頼ったり • テーブル化はUTF-16の方が楽だったりする • 利用頻度の多い文字は大半が2バイト固定長 • (UTF-8だと1~3バイト)
  58. 58. アラビア文字 ラテン文字、漢字に次いで3番目 大変
  59. 59. アラビア文字 • 利用者は結構多い • アラビア語2億3500万人 • ペルシア語7000万人 • ウルドゥー語6100万人 • その割にすごく大変 • 右から左に書く • 隣の文字とつながって字形が変わる インド・ヨーロッパ語族 (アラビア語より英語の方が出自が近い) ヒンディー語と相互理解可能 文字はアラビア文字 聖典を通していろんな言語に幅広く普及 今日は時間の都合で 「右から左」の方だけ説明
  60. 60. 右から左(R to L) アラビア文字 ヘブライ文字
  61. 61. 論理順と表示順 • 論理順 • データ上は「読む方向」で文字を並べる • 表示順 • 右から左に書く言語だと、当然他の言語と向きが逆 例: ‫اللغة‬ (アラビアのアラビア語表記) 論理順: ‫ا‬ ‫ل‬ ‫ل‬ ‫غ‬ ‫ة‬ (a, l, l, g, h) 表示順: (h, g, l, l, a)‫ا‬‫ل‬‫ل‬‫غ‬‫ة‬
  62. 62. 表記 • 入力が大変なので、以下の表記で説明 • abc... など小文字を L to R (我々にとって普通)の文字 • ABC... など大文字を R to L (逆)の文字
  63. 63. R to L仕様 • これも unicode.org に仕様書あり • 英語なので、別にアラビア語がわからなくても手を出せる • UAX #9: Unicode Bidirectional Algorithm • 結構長い… • 複雑になる理由が何点かあり • 明示的な向き指定制御文字 • L/R 混在 • 記号類は周り文字によって向きが変わる • 括弧の対応付け • アラビア文字とヘブライ文字で仕様が違う これはめったに使わないので今日は省略
  64. 64. L/R混在 • アラビア文字の中にあってもラテン文字とかは普通にL to R • 求められる処理のイメージ的には2回ひっくり返す感じ • ちなみにこれを行単位で分けてやる必要あり(先に改行決定が必要) 論理順: ABC abc def DEF 表示順: FED abc def CBA ABC abc def DEF FED fed cba CBA FED abc def CBA どの文字がどの向きかはUCDのBidiPropertyで調べられる
  65. 65. 記号類 • 記号は周りの文字によって向きが変わる • 記号の両側が同じ向きならその向きにそろえる • 両側が違う向きなら「段落の向き」(先頭文字で決まる)にそろえる 論理順: ABC, abc & def, DEF 表示順: FED ,abc & def ,CBA defの右には残らない
  66. 66. 括弧の対応付け • 同種の括弧は常に同じ向きでないとダメ • 括弧内に「段落の向き」と同じものがあれば括弧も「段落の向き」 • なければ直前の文字の向き ABC (abc [ def) DEF ( と ) で対応付け 中にある [ は浮いてる 面倒な処理必須 • どの文字が括弧か判定 • どの文字とどの文字が対応しているか判定※ • 括弧の中身を総なめ ※ 対応情報はUCDのBidiBracketsにあり
  67. 67. アラビア文字とヘブライ文字 • 数字の後ろの%とかの位置が違う • 口頭でそう読むから (= 論理順がそうだから) • ちなみに1234は「4と30と200と1000」みたいな順で読む(リトルエンディアン) • これのせいで、文字の向きには3カテゴリーある • L : L to R な文字 • R : ヘブライ文字 • AL : アラビア文字 (% の向き判定のためにRと分かれてる) 論理順: ABC 1,234% DEF アラビア文字表示順: FED %1,234 CBA ヘブライ文字表示順: FED 1,234% CBA
  68. 68. 絵文字 それ、文字なん? 大体日本のせい
  69. 69. 日本には昔から変な文字がある • 日本の印刷業者は変な文字の印刷を受け付けており… • 漢字がすでに複雑なので、ちょっとくらい記号が増えてもよかろう • 文字コード化されてしまう • 当時は「独自拡張」だった • MacJapanese (Shift_JISコードのMac独自拡張) • そしてUnicodeにも引き継がれてしまう 〠 〄符号点 3004 日本工業規格(JIS)のロゴ (しかも2005年に改訂された古いロゴ) 符号点 3020 顔郵便マーク (民営化前に郵政省が使っていたマスコット) 日本で昔から使っていた 「文字」なので、ひらがな の近くに符号点がある
  70. 70. そこに来てケータイキャリア絵文字 • カラー液晶搭載機の売りにしたかったのかな… • 文字コード化されてしまう • 当時はキャリアごと(DoCoMo, Softbank, au, ...)に独自仕様 • iOS, Androidの日本市場参入に際して標準化 = Unicode 化 • Unicodeの文字にいろいろおかしな概念を持ち込む • 文字の定義に色が含まれる(カラー絵文字) • 国とか人種とか、センシティブな話題を踏み抜く • 絵文字シーケンス(書記素の定義が複雑化) 1F9E1, 1F499~1F49C 1F603, 1F620, 1F61E, 1F616, 1F635
  71. 71. ZWJ ZWJ ZWJ 1F468 1F3FB 200D 1F469 1F3FF 200D 1F466 1F3FD 200D 1F466 1F3FC コード ポイント UTF-16 D83D DC68 D83C DFFB 200D D83D DC69 D83C DFFF 200D D83D DC66 D83C DFFD 200D D83D DC66 D83C DFFC UTF-8 F0 9F 91 A8 F0 9F 8F BB E2 80 8D F0 9F 91 A9 F0 9F 8F BF E2 80 8D F0 9F 91 A6 F0 9F 8F BD E2 80 8D F0 9F 91 A6 F0 9F 8F BC 書記素 11文字 19文字 38バイト 41バイト1文字 絵文字シーケンスの例 1書記素で 11符号点 UTF-8で41バイトの文字 特定の肌色、特定の性別の家族しかないのはおかしい 組み合わせ出来るべきだ 特にセンシティブな話題として、今日は「国旗」の話を 書記素クラスター分割がだいぶ複雑化してる元凶
  72. 72. 国旗 絵文字の中でも特にセンシティブ 「国」にはうかつに触れてはいけない
  73. 73. キャリア絵文字の国旗 • むかしむかしとあるキャリアの独自絵文字に国旗がありました • なぜか10か国 • 「日本人になじみ深い」以上の基準がない • 人口順でもGDP順でも日本からの渡航者数順でもなんでもなく • 例え基準があったとしてもそれは変化する • というか、国という区切り自体日々変化する
  74. 74. キャリア絵文字のUnicode化 • 絵文字を国際規格化したいという段階で困りました • なぜか10か国 • 「日本人になじみ深い」以上の基準がない そんなもの国際規格化できるわけないだろう 「うちの国がなぜ入ってない?」となる
  75. 75. 国名コードを使う • そうだ、国名コードを使おう • 国名コード用にISO 3166-1という規格があるじゃないか • jpとかusとかで国または地域を表す規格 = 🇯 (1F1EF) + 🇵 (1F1F5) = 🇺 (1F1FA) +🇸 (1F1F8) • Region Indicator Symbolっていう • 1F1E6~1E1FF までにA~Zに相当する符号点がある • それを2文字並べることで国旗を表す
  76. 76. 国または地域 • 「国」とは言い切らないのが大事 • 国の定義が難しいから Windowsのオプション画面 台湾 (国名コードtw) 台湾とは? • 中華人民共和国の台湾省? • 中華民国?
  77. 77. Windowsでの国旗絵文字 • 表示されない • というか、絶対表示させない硬い意志を感じる 🇯🇵 🇺🇸 🇨🇳 🇹🇼 • つまり、Microsoftは今でも国旗絵文字に反対 Region Indicator に対応するラテン文字をそのまま表示 (この文字をコピってiOSとかに持っていくと国旗になる)
  78. 78. とある連合王国の1カントリー • 中国台湾省の旗があるのになんでうちの旗はないんだ? と、クレームを入れ始めたのはこいつ 1F3F4 E0067 E0062 E0073 E0063 E0074 E007F 1F3F4 E0067 E0062 E0077 E006C E0073 E007F 𓄿 + gbsct 𓄿 + gbwls 1F3F4 E0067 E0062 E0077 E006C E0073 E007F 𓄿 + gbeng 7 符号点 28バイト (subdivision flag) 終端文字が決まってるだけ 実はまだ良心的な仕様
  79. 79. まとめ
  80. 80. まとめ • Unicode • 符号点(どの文字にどの番号を振るか)については一択 • 符号化(その番号をどういうバイト列にするか)についてはいくつかある • UTF-8が有力なものの、UTF-16もそれなりに使われる • UTF-8, UTF16のバイト数の境目でトラブルを起こしやすい • 西欧の人は文字は1バイトだと思ってる • ごく最近までUTF-8は3バイト以下、UTF-16は2バイト固定扱いされてた • 世界中の文字を収める = 世界中の厄介ごとを引き受ける • 「独自拡張だからいいや」とか言ってると世界標準化される • ローカル規格との互換性 ⇔ 世界中の人が不満なく使える

    Be the first to comment

    Login to see the comments

  • stknohg

    Feb. 15, 2020
  • takeakitsutsumi

    Feb. 15, 2020
  • YoshioHara

    Feb. 15, 2020

https://edge.connpass.com/event/161663/ にて登壇。 今現在、Unicodeという文字規格には13万個以上の文字が収録されています。それぞれの文字には文字のカテゴリー、文字と文字の連結方法、左右どちらから読むかなど、様々な付帯情報も定められています。英語でドキュメントがあり、例えばアラビア語を読めなくてもアラビア文字のレンダリング処理を書ける程度には詳細な仕様が書かれています。 本セッションでは、このUnicodeの仕様の概要と、それをプログラム(主にUnity上でのC#を想定)的に処理する際の注意点などについて説明します。

Views

Total views

3,417

On Slideshare

0

From embeds

0

Number of embeds

205

Actions

Downloads

8

Shares

0

Comments

0

Likes

3

×