Ruby にて文字と Unicode コードポイントの相互変換を行う
Unicode のコードポイントを指定して文字を得たり、逆にある文字のコードポイントを調べたり、ということをする機会は結構多いと思います。 が、Ruby でそれをやる方法をぐぐってもあまり上位に情報が出てこないなー、と思ったので簡単にまとめておきます。
Unicode コードポイントとは
そもそも Unicode コードポイントとは何か。 Unicode というのは世界中の文字が集められた文字集合であり、Unicode に収録されている文字には順番に番号が振られています。 この番号のことをコードポイントといいます。 あるコードポイントが指す文字を表現するときに "U+" という文字の後ろに 16 進数表記のコードポイントを書いて表すことがあります。 例えば、コードポイント 0x3041 が指す文字 (ひらがなの 「あ」) を U+3041 と書いて表します。
各文字とコードポイントの関係は以下のページで確認できます。
Array#pack メソッド, String#unpack メソッド (Ruby 1.8, 1.9)
Ruby 1.8 で Unicode コードポイントから文字を取得したり、文字から Unicode コードポイントを取得するには, Array#pack
メソッドと String#unpack
メソッドを使う必要があります *1。 ここでいう 「文字」 とは String
オブジェクト (さらにいうと、UTF-8 エンコードされたバイト列) であり、「Unicode コードポイント」 とは Unicode コードポイントの値を表す Fixnum
オブジェクトです。
Array#pack
のテンプレート文字として "U"
を指定すると、配列中の Fixnum
オブジェクトを Unicode コードポイントだとみなして、文字列にパックしてくれます。
[ 0x6587, 0x5B57 ].pack( "U*" ) #=> "文字"
逆に、String#unpack
のテンプレート文字として "U"
を指定すると、文字列を UTF-8 エンコードされたバイト列だとみなして、その Unicode コードポイントを要素とする配列を得ることができます。
"文字".unpack( "U*" ) #=> [ 25991, 23383 ] # 25991 == 0x6587, 23383 == 0x5B57
ここで注意しなければならないのは、Array#pack("U*")
の結果得られる文字列のエンコーディングは UTF-8 である、ということです。 また、String#unpack("U*")
で変換元となる文字列のエンコーディングも UTF-8 でなければいけません。
これらの方法は Ruby 1.8 だけでなく、1.9 でも使用できます。 (Ruby 1.9 でもエンコーディングに気をつけなければいけません。)
String#ord メソッド, Integer#chr メソッド, Unicode エスケープ (Ruby 1.9)
Ruby 1.9 では、String#ord
メソッドと Integer#chr
メソッドを使って文字列と Unicode コードポイントの変換ができます。 また、Unicode エスケープを使うことで、文字列リテラル中に Unicode コードポイントを指定して文字を表すことができます。
String#ord
メソッドは、Unicode 系のエンコーディング (UTF-8 や UTF-16BE など) の String
オブジェクトで使うと、その文字列の最初の文字の Unicode コードポイントが得られます。
str = "文".encode( "UTF-8" ) str.ord #=> 25991
Unicode 系でないエンコーディングの場合、その文字に対応するバイト列を数値に変換したものが得られます。
str = "文".encode( 'EUC-JP' ) str.ord #=> 51896 # 以下と同じ str[0].force_encoding( 'ASCII-8BIT' ).unpack( 'C*' ).inject{ |i,j| ( i << 8 ) + j } #=> 51896
Integer#chr
は、String#ord
と逆のことを行います。 ここで注意しないといけないのは、Integer#chr
の実行時にエンコーディングを指定しないといけないということです。 (自動的に UTF-8 になるわけではない。)
25991.chr( "UTF-8" ) #=> "文"
また、文字列リテラル中に Unicode エスケープを入れることで、静的に Unicode コードポイントを文字に変換できます。 Unicode エスケープにより生成される文字のエンコーディングは常に UTF-8 です。
Unicode エスケープは \uXXXX
または \u{XXXX}
の形式で、XXXX は Unicode コードポイント (16 進数表記) です。 前者の形式の場合、コードポイントは 4 桁で書かなければいけません。 後者の形式の場合、4 桁でなくてもよく、またスペース区切りで複数のコードポイントを連続して書くことができます。
"\u6587" #=> "文" # 0x6587 == 25991 "\u{41 42 43}" #=> "ABC" # \u{XX} 形式の場合, 4 桁でなくてもよく, 複数文字を連続して書ける
Unicode エスケープによる文字のエンコーディングが UTF-8 であるので、ソースエンコーディングが UTF-8 ではない場合に、"あいうえ\u6587"
のような文字列リテラルを書くとエラーが発生しますのでご注意ください。
また、文字列中の各文字に対して繰り返し ord
メソッドを作用させてブロックに渡すイテレーションメソッドとして String#each_codepoint
メソッド (別名 String#codepoints
) もあります。
"あいうえお".each_codepoint { |cp| $stdout << "U+" << cp.to_s(16) << " " } # U+3042 U+3044 U+3046 U+3048 U+304a
参考文献
この記事で述べた各メソッドについてはそれぞれ以下のドキュメントを参照してください。