String の encode 周りのメソッドについてまとめました。
文字コード関連メソッド
String クラスには以下の文字コード関連メソッドがあります。
メソッド | 機能 |
---|---|
String#encode | 指定したエンコーディングに変換した文字列を返す |
String#encode! | 指定したエンコーディングに変換して自身を置き換える |
String#encoding | 文字列のエンコーディング情報を表現した Encoding オブジェクトを返す |
String#force_encoding | 文字列の持つエンコーディング情報を指定した Encoding に変える |
String#valid_encoding? | 文字列が持っているエンコーディング情報と内容が妥当であるか判定 |
encodeとencode!メソッド
encode
とencode!
メソッドは以下のように復数の引数を受け取ります。
String#encode(dst_encoding [, options]) String#encode(dst_encoding, src_encoding [, options]) String#encode([options])
引数の 1 つ目は変換後の文字コード、2 つ目は変換元の文字コードを意味します。文字コードは文字列、又は Encoding オブジェクトを指定します。
sjis_str = "\x82\xa0" # Shift_JISで"あ"になるバイト列 #=> "\x82\xA0" sjis_str.encoding #=> #<Encoding:UTF-8> # 第2引数にはselfの UTF-8 が使われるため例外が発生 # Encoding::InvalidByteSequenceError: "\x82" on UTF-8 sjis_str.encode(Encoding::UTF_16BE, Encoding::Shift_JIS) # 変換元の文字コードを指定すると成功 #=> "\u3042"
2 つ目の引数を省略した場合はself
のエンコーディング情報が使われます。
utf8_str = "\xe3\x81\x82" # UTF-8で"あ"になるバイト列 #=> "あ" utf8_str.encoding #=> #<Encoding:UTF-8> utf8_str.encode(Encoding::Shift_JIS) # 第2引数にはselfの UTF-8 が使われる #=> "\x{82A0}"
引数を 2 つとも省略した場合は Encoding.default_internal が変換後の文字コードとなり、nil
の場合は変換は行われません。
また、オプションでinvalid: :replace
とundef: :replace
が指定されたものと見なされ、このオプションは変更できません。
Encoding.default_internal = Encoding::UTF_8 sjis_str = "\x82\xa0" # Shift_JISで"あ”になるバイト列 sjis_str.encoding #=> #<Encoding:UTF-8> sjis_str.encode # この場合は encode("UTF-8", "UTF-8") を実行するのと同じ #=> "\x82\xA0" sjis_str.force_encoding(Encoding::Shift_JIS) # 正しいエンコーディング情報を指定 sjis_str.encode # この場合は encode("UTF-8", "Shift_JIS") になる #=> "あ" utf8_str = "\xe3\x81\x82".force_encoding(Encoding::Shift_JIS) # UTF-8のバイト列にShift_JISのエンコーディング情報を指定 #=> "\x{E381}\x82" utf8_str.encode(replace: 'hoge') # encode("UTF-8", "Shift_JIS") になって変換元が違うので変換できない #=> "縺hoge" # InvalidByteSequenceが発生したバイト文字が置換される
同じエンコーディングを指定した場合
encode
で変換後と変換元の文字コードに同じエンコーディングを指定した場合どのような挙動になるのか分からなかったのですが、実際に試してみたところforce_encoding
と同じようにエンコーディング情報を書き換えているだけで実際の変換はしてないと推測してます。
sjis_str.encoding #=> #<Encoding:Shift_JIS> new_str = sjis_str.encode(Encoding::UTF_8, Encoding::UTF_8) #=> "\x82\xA0" # 本来なら変換元にUTF-8を指定してるのでInvalidByteSequenceが発生するはず new_str.encoding #=> #<Encoding:UTF-8> # 文字コード情報のみ書き換わっている sjis_str.force_encoding(Encoding::UTF_8) => "\x82\xA0" sjis_str.encoding => #<Encoding:UTF-8> # 上記と同じ挙動
発生する例外
変換元のエンコーディングにおいて不正なバイトがあった場合に、Encoding::InvalidByteSequenceError
が発生する。
sjis_str = "\x82\xa0" sjis_str.encode(Encoding::Shift_JIS, Encoding::UTF_8) # Encoding::InvalidByteSequenceError: "\x82" on UTF-8 # "\x82"は UTF-8 で解釈できない
変換先のエンコーディングにおいて文字が定義されていない場合に、Encoding::UndefinedConversionError
が発生する。
str = "テスト" str.encode(Encoding::UTF_8, Encoding::Shift_JIS) # Encoding::UndefinedConversionError: "\x86\xE3" from Shift_JIS to UTF-8 # Shift_JISの"\x86\xE3"はUTF-8で定義されてない
変換オプション
encode
での変換時には以下のオプションが使用できます。
オプション | デフォルト | 機能 |
---|---|---|
:invalid | nil | :replace を指定すると変換元の不正なバイト文字を置換する |
:undef | nil | :replace を指定すると変換先で定義されていない文字を置換する |
:replace | U+FFFD | ? | 置換文字を指定する |
:fallback | - | 未定義の文字に対する置換文字をHash Proc Method で渡す |
:xml | - | 文字列を XML の CharData として適するように処理 |
:universal_newline | - | CR 改行および CRLF 改行を LF 改行に置き換える |
:cr_newline | - | LF 改行を CR 改行に置き換える |
:crlf_newline | - | LF 改行を CRLF 改行に置き換える |
文字コードチェック
与えられた文字列の文字コードがUTF-8
なのかShift_JIS
なのかの確認は次のようにできます。force_encoding
でチェックしたい文字コード情報に書き変えてvalid_encoding?
を実行しています。dup
を使用しているのはforce_encoding
が self を書き換えるためです。
utf8_str = "\xe3\x81\x82" utf8_str.dup.force_encoding(Encoding::UTF_8).valid_encoding? #=> true utf8_str.dup.force_encoding(Encoding::Shift_JIS).valid_encoding? => false
エンコーディングについては 多言語化 (Ruby 2.4.0) も参考になります。