文字ときどきRuby / Character and Ruby
by
とみたまさひろ
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
文字ときどきRuby nagano.rb #13 2023-12-09 とみたまさひろ 1
Slide 2
Slide 2 text
自己紹介 • とみたまさひろ • • https://twitter.com/tmtms https://blog.tmtms.net 2
Slide 3
Slide 3 text
これは同じ文字? 直 直 3
Slide 4
Slide 4 text
フォントが違うだけで同じ文字 直 U+76F4 日本語フォント 直 U+76F4 中国語フォント 4
Slide 5
Slide 5 text
これの違いと同じ 直 直 5
Slide 6
Slide 6 text
コンピュータで扱う文字は文字ごとに番号(コードポイント) が振られていてプログラムから見たときは同じコードポイ ントであれば同じ文字 6
Slide 7
Slide 7 text
Rubyでコードポイントを知る > '直'.ord.to_s(16) "76f4" > 'ほげ'.chars.map{_1.ord.to_s(16)} ["307b", "3052"] > 'ほげ'.unpack('U*').map{_1.to_s(16)} ["307b", "3052"] 7
Slide 8
Slide 8 text
これは同じ文字? 令 令 8
Slide 9
Slide 9 text
違う文字 令 U+4EE4 CJK統合漢字 令 U+F9A8 CJK互換漢字 9
Slide 10
Slide 10 text
正規化すれば同じ文字 String#unicode_normalize > '令'=='令' false > '令'=='令'.unicode_normalize true 10
Slide 11
Slide 11 text
正規化でこんなことも 使いようによっては便利 '0'.unicode_normalize(:nfkc) => '0' '①'.unicode_normalize(:nfkc) => '1' 'ア'.unicode_normalize(:nfkc) => 'ア' 'パ'.unicode_normalize(:nfkc) => 'パ' '㌖'.unicode_normalize(:nfkc) => 'キロメートル' 11
Slide 12
Slide 12 text
これは同じ文字? 令 � 12
Slide 13
Slide 13 text
異体字 令 U+4EE4 � U+4EE4 U+E0102 13
Slide 14
Slide 14 text
基底文字に異体字セレクタを追加することで プレーンテキストでも文字の見た目を 指定することができる 14
Slide 15
Slide 15 text
異体字セレクタ � U+4EE4 U+E0102 ←これ U+E0100〜U+E01EF が異体字セレクタ 対応システムと対応フォントが必要 15
Slide 16
Slide 16 text
異体字セレクタ 異体字セレクタセレクタが便利 https://747.github.io/vsselector/#!/ja/908a 16
Slide 17
Slide 17 text
異体字セレクタ unicode_normalize では消えないので U+E0100〜U+E01EF を消す "\u4ee4\u{e0102}".gsub(/[\u{e0100}-\u{e01ef}]/, '') 17
Slide 18
Slide 18 text
「髙」と「﨑」 > '高' == '髙' false > '崎' == '﨑' false 18
Slide 19
Slide 19 text
「髙」 Unicode では「髙」は「高」の異体字ではなく別の文字 別の文字なので異体字セレクタにもない SJIS(Windows-31J)でも別の文字 でも JIS では「髙」という文字は存在しない 「高」の異体字扱い 19
Slide 20
Slide 20 text
「髙」 > '髙'.encode('Windows-31J') "\x{FBFC}" > '髙'.encode('SJIS') # SJIS は Windows-31J の別名 "\x{FBFC}" > '髙'.encode('Shift_JIS') # Shift_JIS と SJIS は異なる # `encode': U+9AD9 from UTF-8 to Shift_JIS # (Encoding::UndefinedConversionError) 20
Slide 21
Slide 21 text
「髙」 Unicode 上は別の文字なので同一文字として扱わなけれ ばいいんだけど、人名検索とかだと同一文字として扱いた いこともあるかもしれないのでむずかしい 21
Slide 22
Slide 22 text
「﨑」 CJK互換漢字 「令」と同じ だけど unicode_normalize では「崎」にならない > '﨑'.unicode_normalize "﨑" # CJK互換漢字は普通は正規化できる > '福'.unicode_normalize "福" 22
Slide 23
Slide 23 text
「﨑」 これも「髙」と同じく変換するには個別対応が必要そう U+FA11(﨑)はU+5D0E(崎)に統 合漢字ブロックの異体字を持つが、字体 差が大きいとみなされ統合の範疇とされ ていない。 CJK互換漢字 - Wikipedia 23
Slide 24
Slide 24 text
これは同じ文字? へ ヘ 24
Slide 25
Slide 25 text
別の文字だけど日本語のバグ へ 平仮名 ヘ 片仮名 25
Slide 26
Slide 26 text
この文字数は? 26
Slide 27
Slide 27 text
国旗は2文字 U+1F1EF U+1F1F5 + 国コードを国旗用文字2文字で書くと国旗になる + = 27
Slide 28
Slide 28 text
3人家族は1文字 U+1F46A 「FAMILY」という絵文字 28
Slide 29
Slide 29 text
4人家族は7文字 U+1F468 MAN U+200D ゼロ幅接合子 U+1F469 WOMAN U+200D ゼロ幅接合子 U+1F467 GIRL U+200D ゼロ幅接合子 U+1F466 BOY 29
Slide 30
Slide 30 text
濁点つき文字 ぱ U+3071 ぱ U+306F U+309A 「は」+合成用半濁点文字 (これは unicode_normalize(:nfc) で1文字の「ぱ」になる) 30
Slide 31
Slide 31 text
囲み文字 a⃝ U+0041 U+20DD a⃤ U+0041 U+20E4 a⃞ U+0041 U+20DE a ⃣ U+0041 U+20E3 31
Slide 32
Slide 32 text
人間の肌色と髪型 U+1F9D1(大人) + U+1F3FB(明るい肌色) + U+1F3FC(やや明るい肌色) U+200D U+1F9B0(赤毛) + U+1F3FE(やや濃い肌色) U+200D U+1F9B2(坊主頭) 32
Slide 33
Slide 33 text
文字数とは? 33
Slide 34
Slide 34 text
プログラム的に自然なのは コードポイントの数 でも人にはわかりにくい > ' '.size 10 34
Slide 35
Slide 35 text
書記素 より 「人が1文字として見える文字」みたいな 書記素(しょきそ、英: grapheme)と は、書記言語において意味上の区別を 可能にする最小の図形単位をいう 書記素 - Wikipedia 35
Slide 36
Slide 36 text
Ruby で書記素を扱う String#grapheme_clusters > ' '.size 10 > ' '.grapheme_clusters [" ", " ", " "] > ' '.grapheme_clusters.size 3 36
Slide 37
Slide 37 text
Ruby で書記素を扱う 正規表現 \X > ' '.scan(/./) [" ", " ", " ", " ", "", " ", "", " ", "", " "] > ' '.scan(/\X/) [" ", " ", " "] 37
Slide 38
Slide 38 text
まとめ • ユニコードはカオス • 文字列を比較するときは正規化 • 文字数はコードポイントなのか書記素なのかを考える 38