Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyの文字化け・エンコーディングエラーを3ステップで解決する方法

文字列のエンコーディングを本気で考えるのは、たいてい問題が発生したときです。例外トラッカーを開いたら、次のようなエラーが出力されていた――そんな経験はないでしょうか。

Encoding::InvalidByteSequenceError: "\xFE" on UTF-8

あるいは、「they're」と表示されるはずの文字列が「they’re」のように文字化けすることもあります。

では、エンコーディングが壊れてしまったとき、どこに原因があるのかをどう突き止めればよいのでしょうか?そして、それをどう直せばいいのでしょうか?本記事では、その具体的な手順を解説します。

エンコーディングとは何か?

エンコーディングが文字列に対して何を行っているのかイメージできれば、こうしたバグは格段に修正しやすくなります。

まず、文字列はバイト(小さな数値)の配列だと考えることができます。

irb(main):001:0> "hello!".bytes
=> [104, 101, 108, 108, 111, 33]

この例では、104h を、33! を表しています。

ところが、英語ではあまり使われない文字を扱うと、少し複雑になります。

irb(main):002:0> "hellṏ!".bytes
=> [104, 101, 108, 108, 225, 185, 143, 33]

今度は、どの数字がどの文字に対応しているのか一目では分かりません。 は1バイトではなく、[225, 185, 143] というバイトのグループで表現されているからです。それでも、バイトと文字の間には必ず対応関係があります。そして、その対応関係を定義しているのが文字列のエンコーディングなのです。

同じバイト列でも、エンコーディングを変えて解釈するとどうなるか見てみましょう。

# 特殊文字を含むISO-8859-1の文字列を試してみる!
irb(main):003:0> str = "hellÔ!".encode("ISO-8859-1"); str.encode("UTF-8")
=> "hellÔ!"

irb(main):004:0> str.bytes
=> [104, 101, 108, 108, 212, 33]

# 同じ文字列をISO-8859-5として解釈すると?
irb(main):005:0> str.force_encoding("ISO-8859-5"); str.encode("UTF-8")
=> "hellд!"

irb(main):006:0> str.bytes
=> [104, 101, 108, 108, 212, 33]

バイト列自体は一切変わっていません。それなのに、表示される内容はまったく違うものになりました。バイト列を変更せずに、エンコーディングだけを切り替えるだけで文字列の出力が変わってしまうのです。

さらに重要な点として、すべての文字列がすべてのエンコーディングで表現できるわけではありません。

irb(main):006:0> "hi∑".encode("Windows-1252")
Encoding::UndefinedConversionError: U+2211 to WINDOWS-1252 in conversion from UTF-8 to WINDOWS-1252
	from (irb):61:in `encode'
	from (irb):61
	from /usr/local/bin/irb:11:in `<main>'

ほとんどのエンコーディングは扱える文字種が限られており、あらゆる文字をカバーできません。あるエンコーディングの文字が別のエンコーディングに存在しない場合や、Rubyが2つのエンコーディング間の変換方法を判断できない場合に、このようなエラーが発生します。

このエラーは、encode メソッドに追加オプションを渡すことで回避できます。

irb(main):064:0> "hi∑".encode("Windows-1252", invalid: :replace, undef: :replace)
=> "hi?"

invalidundef オプションを指定すると、変換できない文字が別の文字に置き換えられます。デフォルトの置き換え文字は ? です(Unicodeへの変換の場合は になります)。

ただし注意が必要なのは、encode による置き換えでは情報が失われる可能性があるという点です。どのバイトが ? に置き換わったのかを把握できなくなります。とはいえ、データをどうしてもそのエンコーディングに合わせたい場合、壊れた状態のままにしておくよりも、多少のデータ損失を受け入れるほうが得策でしょう。

ここまでで、エンコーディングを理解するために欠かせない3つの文字列メソッドを紹介しました。

  • encode:文字列を別のエンコーディングへ変換する(文字を新しいエンコーディングにおける等価な文字へ変換)

  • bytes:文字列を構成するバイト列を表示する

  • force_encoding:バイト列を別のエンコーディングで解釈するとどうなるかを確認する

encodeforce_encoding の最大の違いは、encode はバイト列を書き換える可能性があるのに対し、force_encoding はバイト列を一切変更しないという点です。

エンコーディングバグを修正する3ステップの手順

ほとんどのエンコーディング問題は、次の3つのステップで解決できます。

ステップ1:文字列が実際にはどのエンコーディングなのかを特定する

一見簡単そうですが、落とし穴があります。文字列があるエンコーディングだと自称していても、実際にそうであるとは限らないのです。

irb(main):078:0> "hi\x99!".encoding
=> #<Encoding:UTF-8>

これは明らかにおかしいですね。本当にUTF-8であれば、あのような不自然なバックスラッシュ付きの数値は含まれないはずです。では、正しいエンコーディングをどう特定すればいいのでしょうか?

多くの古いソフトウェアは特定のデフォルトエンコーディングを使い続けるため、入力元を調べることが有力な手掛かりになります。Wordからコピー&ペーストされたデータでしょうか?であれば Windows-1252 の可能性が高いです。古いWebサイトやファイルから取得したデータなら、ISO-8859-1かもしれません。

また、Wikipediaなどで公開されているエンコーディング対応表を参照するのも非常に有効です。対応表で未知のバイト値に対応する文字を調べれば、文脈上意味が通るかどうかを確認できます。

先ほどの例でいうと、Windows-1252の対応表によれば、バイト 99 は「™」記号を表します。一方、バイト 99 はISO-8859-1には存在しません。ここで ™ という文字が文脈的に自然であれば、入力データは Windows-1252 だったと推測して先へ進めます。そうでなければ、より納得感のある文字が見つかるまで調査を続けましょう。

ステップ2:文字列をどのエンコーディングにしたいか決める

このステップは簡単です。特別な理由がない限り、文字列はUTF-8でエンコードしておくのがベストです。

Rubyでもうひとつよく使われるエンコーディングとして、ASCII-8BITがあります。ASCII-8BITではすべての文字が1バイトで表現されるため、str.chars.length == str.bytes.length が常に成り立ちます。そのため、文字列内の個々のバイトを厳密に制御したいケースでは、ASCII-8BITが適していることもあります。

ステップ3:ステップ1のエンコーディングからステップ2のエンコーディングへ再エンコードする

この変換は encode メソッドで行えます。先ほどの例では、文字列は現在 Windows-1252 エンコーディングであり、それをUTF-8にしたいわけです。非常にシンプルですね。

irb(main):088:0> "hi\x99!".encode("UTF-8", "Windows-1252")
=> "hi™!"

これで完璧です。(とはいえ、引数に指定するエンコーディングの順序は、筆者にはいつも逆に感じられてしまうのですが……)。

まとめ:irbで実際に触れて感覚をつかもう

同じバイト列が異なる解釈を持つことを想像するのは、頭がこんがらがる作業です。特に、そのうちの1つが壊れた状態であるとなおさらです。しかし、エンコーディングに慣れるための確実な方法があります。それは、実際に触って遊んでみることです。

irb コンソールを開き、encodebytesforce_encoding を自由にいじってみてください。encode が文字列を構成するバイトをどう変化させるのかを観察し、各エンコーディングの特徴について直感を養いましょう。エンコーディングに慣れ、今回の3ステップを実践できるようになれば、以前なら何時間もかかっていた問題を、わずか数分で解決できるようになるはずです。

最後に、このような「手を動かして学ぶ」習慣の作り方を詳しく知りたい方は、著者の書籍の無料サンプル章をぜひチェックしてみてください。コンソールで実際に壊しながら学ぶのは、こうした概念を身につけるための本当に楽しく効果的な方法です。


  1. Windows 10アップデートの問題を解決する方法【エラー別・原因別の対処法まとめ】

    本記事では、Windows 10のアップデート中にダウンロードやインストールでエラーが発生する問題、またはWindows 10を最新バージョン(ビルド)へアップグレードする際に起こるさまざまなトラブルを解決するための、複数の対処方法を紹介します。 多くのWindows 10ユーザーが重要な更新プログラムのインストール時に問題に直面していますが、その際表示される「問題が発生しました – Windows 10のインストールに失敗しました」や「Windows 10への機能更新プログラムのインストールに失敗しました」といったエラーメッセージは、残念ながら問題解決の手がかりにはなりません。(普段正常に動

  2. Windows 10でBluetoothがつながらない・接続できないときの対処法7選

    Bluetoothを使えば、ケーブルなしでWindows 10のPCに周辺機器やアクセサリをワイヤレスで接続できます。普段は問題なく動作しますが、接続できない・不安定になるなどのトラブルが起こることもあります。この記事では、Windows 10 PCのBluetooth接続の問題を解決するための対処法を7つのステップでわかりやすく解説します。1. Bluetoothが有効になっているか確認する基本的な確認ですが、意外と見落とされがちなのがBluetoothの有効/無効設定です。デスクトップ右下にある通知アイコン(通知センター)をクリックまたはタップして展開しましょう。Bluetoothアイコン