Ruby で,特定の文字(や文字列)について
- バイト表現
- コードポイント
- Unicode 名
- Unicode におけるブロック
- Unicode における一般カテゴリー
などが知りたいときに使える小技を紹介しよう。
整数を 16 進法の文字列へ
まず準備として,バイト値(0〜255 の整数)やコードポイントの整数を,16 進法の文字列にするやり方について述べる。
0〜255 のバイト値を 2 桁ゼロ埋め大文字 16 進表示の文字列にしたければ,String#% を用いて
p "%02X" % 92 # => "5C"
のようにする。
フォーマット指定の 02 は「ゼロ埋めで 2 桁」を意味する。X は「大文字の 16 進表記」を意味する。小文字がよければ x とする。
バイト列の 16 進表示
文字列のバイト表現は,String#bytes で得られる。
よって,文字列のバイト列を 16 進表示したければ
str = "ほげ"
puts str.bytes.map{ "%02X" % _1 }.join(" ")
# => E3 81 BB E3 81 92
などと書けばよい。
ファイルの先頭 10 バイトがどうなっているか見たければ
head = File.binread("hoge.pdf", 10)
puts head.bytes.map{ "%02X" % _1 }.join(" ")
のようにする。(binread で読み込むこと!)
コードポイント列
Unicode では,コードポイントは U+3042 のように,「U+」に続けて 4〜6 桁の 16 進法で表すことになっている。
数値として 0xFFF 以下であっても 3 桁以下にはせず,ゼロ埋めするルールだ。
文字列のコードポイント列は,String#codepoints で得られる。
返り値は整数の配列である。
よって,文字列のコードポイント列表示は
str = "𩸽1尾"
puts str.codepoints.map{ "U+%04X" % _1 }.join(" ")
# => U+29E3D U+0031 U+5C3E
のようにして得られる。
フォーマット指定について補足すると,04 は「ゼロ埋め 4 桁」なのだが,値が 4 桁で収まらないときは 5 桁以上になってくれる。
だから Unicode のコードポイント表示のルールどおりになる。
Unicode 情報
Unicode 名
「é」という文字(U+00E9)の Unicode 名は,「LATIN SMALL LETTER E WITH ACUTE」となっている。
これは unicode-name という gem を使えば分かる。
require "unicode/name"
puts Unicode::Name.readable("é")
# => LATIN SMALL LETTER E WITH ACUTE
一般カテゴリー
Unicode では,各文字が何らかの「一般カテゴリー(General Category)」に属す。
3 や 3(全角数字の 3)や ۳(アラビア文字の 3)などの文字が属す一般カテゴリーは「Decimal_Number」だ。
(人間が見る文字列としてはこのアンダースコアがスペースになっていたりするが,定義上はアンダースコアであるらしい)
一般カテゴリー名には短い形もあって,「Decimal_Number」の場合は「Nd」である。
いわゆる半角スペースと全角スペースの一般カテゴリーはいずれも「Space_Separator」(Zs)だ(括弧内が短い形)。
一般カテゴリー名は正規表現の \p{ } に使えるので,これが簡単に知れるとありがたい。
これも unicode-categories という gem で調べることができる。
require "unicode/categories"
puts Unicode::Categories.category("3", format: :long)
# => Decimal_Number
puts Unicode::Categories.category("3")
# => Nd
スクリプト(用字系)
スクリプト(script)というのは以下のようなもの
- 「あ」→ 平仮名
- 「Q」→ ラテン文字
- 「Я」→ キリル文字
- 「아」→ ハングル
これも unicode-scripts という gem で調べられる。
require "unicode/scripts"
puts Unicode::Scripts.script("あ")
# => Hiragana
ブロック
Unicode ブロックも unicode-blocks という gem で調べられる。
require "unicode/blocks"
puts Unicode::Blocks.block("A")
# => Basic Latin
puts Unicode::Blocks.block("魚")
# => CJK Unified Ideographs
puts Unicode::Blocks.block("𩸽")
# => CJK Unified Ideographs Extension B
puts Unicode::Blocks.block("あ")
# => Hiragana
上の例で「あ」は「Hiragana」というブロックになっている。
「あ」はスクリプトも「Hiragana」だった。
スクリプトとブロックの違いに注意
さきほどの例のように,スクリプト名とブロック名が一致していることがある。
そのためしばしば混同されてしまう。
気をつけよう。
「㍍」はスクリプトが「Katakana」だが,ブロックは「CJK Compatibility」である。
正規表現の \p{ }(プロパティー名による文字クラス)で片仮名を指定したいとき
-
\p{Katakana}(スクリプトを指定) -
\p{In_Katakana}(ブロックを指定;ブロック名の前にIn_を付ける)
の違いに注意する必要がある。
Katakana ブロックは U+30A0〜U+30FF の範囲であって,ここには半角片仮名や「ㇰ」といったものは含まれない。
長さ
文字列のバイト長(何バイトからなるか)は String#bytesize で分かる:
# encoding: UTF-8
p "あ".bytesize # => 3
p "𩸽".bytesize # => 4
単に「文字列の長さ」といったときは,「何文字からなるか」という値だ。
これは String#length で分かる:
p "あ".length # => 1
p "𩸽".length # => 1
しかし,この「何文字からなるか」は自明な概念ではない。
String#length はコードポイントの数を返すので,もし一つの文字が複数のコードポイントからなるのであれば,素朴な文字数よりも大きい値になりうる。
たとえば,「ガ」という文字は,Unicode 正規化方式の違いにより,以下の二つの表し方ができる:
- Unicode 正規化方式 C: U+30AC
- Unicode 正規化方式 D: U+30AB U+3099
後者の場合,U+30AB(「カ」)と U+3099(合成用濁点)の並びだ。
よって,length で調べた長さはそれぞれ 1, 2 となる。