LoginSignup
10
5

More than 3 years have passed since last update.

Rubyのencodeとforce_encodingの違い

Posted at

はじめに

Rubyのencodeメソッドとforce_encodingメソッドの違いをご存じでしょうか?
まずはRubyのリファレンスマニュアルを見てみましょう。

encode

self を指定したエンコーディングに変換した文字列を作成して返します。(以下、省略)

force_encoding

文字列の持つエンコーディング情報を指定された encoding に変えます。
このとき実際のエンコーディングは変換されず、検査もされません。

encodeはその名の通り文字列を変換してくれて、force_encodingはエンコーディング情報(?)を変えるけど、文字列自体は変換されないと。エンコーディング情報が何かは分かりませんが、とりあえず動かしてみましょう。

動作確認

検証用に「テスト」という文字列をiso-2022-jpという文字符号化方式に変換したものを用意します。メールでUTF-8を期待していたところに別の文字符号化方式の文字列が送られてきたイメージです。

puts "テスト".encode('iso-2022-jp')

出力結果

$B%F%9%H(B

以降の内容を試す場合は下記のリンク先からエンコードされた文字列をコピーしてご使用ください。
https://paiza.io/projects/jCRDa1-PFB06JTcOZTGJ-g

この文字列をUTF-8の元の文字列に戻してみましょう。

まずはencodeを試してみます。

puts "$B%F%9%H(B".encode('utf-8')

出力結果

$B%F%9%H(B

あれ、何も変わらない・・・。

次にforce_encoding

puts "$B%F%9%H(B".force_encoding('utf-8')

出力結果

$B%F%9%H(B

こちらも変わらず・・・。

この文字列を元に戻すにはどうすれば良いのでしょうか?
以下に、前提となる知識と変換方法をご紹介します。

エンコーディング情報

force_encodingの説明の中にもありましたが、文字列はエンコーディング情報を持ちます。
文字列のエンコーディング情報を確認するにはencodingメソッドで確認することができます。

puts "テスト".encoding

出力結果

UTF-8

encodingの返り値として、Encodingオブジェクトを返します。Encodingクラスは文字エンコーディング(文字符号化方式)のクラスです。encodeforce_encodingの実行後に確認してみると違いが分かりそうですね。

文字列とバイト

たとえば「あ」という文字列はバイトで表すと3つの数字が返ってきます。

puts "#{"あ".bytes}"

出力結果

[227, 129, 130]

それぞれの数字を16進数に変換すると[e3 81 82]となり、この数字はUTF-8の「あ」と一致します。(参考)
このように文字はそれぞれバイト列が決まっているので、バイト列の数値が変化していれば文字が変わったと判断できそうですね。

対象文字列の確認

さて、これらを踏まえた上で改めて先程の文字列を見てみましょう。

str = "$B%F%9%H(B"
puts "文字列:#{str}"
puts "バイト列:#{str.bytes}"
puts "エンコーディング情報:#{str.encoding}"

出力結果

文字列:$B%F%9%H(B
バイト列:[36, 66, 37, 70, 37, 57, 37, 72, 40, 66]
エンコーディング情報:UTF-8

エンコーディング情報を見るとUTF-8となっているので、encodeUTF-8に変換しようとしても何も変わりません。また、force_encodingを実行しても、元々のエンコーディング情報がUTF-8のためこちらも何も変わりません。これによって最初の動作確認で何も変化しなかった理由が分かりました。では、どうすれば変換できるのでしょうか?

変換方法

以下の2ステップで変換します。
1. force_encodingでエンコーディング情報をiso-2022-jpに変換する
2. encodeで文字列をUTF-8に変換する

str = "$B%F%9%H(B"
puts "オリジナル"
puts str
puts str.encoding
puts "#{str.bytes}"

puts "force_encoding"
force_encode_str = str.force_encoding('iso-2022-jp')
puts force_encode_str
puts force_encode_str.encoding
puts "#{force_encode_str.bytes}"

puts "encode"
encode_str = force_encode_str.encode('utf-8')
puts encode_str
puts encode_str.encoding
puts "#{encode_str.bytes}"

実行結果

オリジナル
$B%F%9%H(B
UTF-8
[27, 36, 66, 37, 70, 37, 57, 37, 72, 27, 40, 66]

force_encoding
$B%F%9%H(B
ISO-2022-JP
[27, 36, 66, 37, 70, 37, 57, 37, 72, 27, 40, 66]

encode
テスト
UTF-8
[227, 131, 134, 227, 130, 185, 227, 131, 136]

force_encodingで文字列に期待するコーディング情報に変換し、その後、encodeで期待する文字符号化方式(今回であればUTF-8)で文字列を変換しました。文字列はもちろんのこと、バイト列も変換されているのが確認できますね。

まとめ

encode

  • 文字列を指定したエンコーディングに変換
  • 文字列がもつエンコーディング情報についても、指定したエンコーディングに変換
  • エンコーディング情報と指定したエンコーディングが同一の場合、変化しない

force_encoding

  • 文字列自体は変化しない
  • 文字列がもつエンコーディング情報を指定したエンコーディングに変換
  • encodeの前処理として使いそう

参考

encoding - What is the difference between #encode and #force_encoding in ruby? - Stack Overflow

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5