Herokuでは、アプリがRedisから「niño」というメッセージを受信すると、実際には4バイトを取得しています。
0x6e 0x69 0xf1 0x6f
これは、 ISO-8859-1として解釈される場合、文字n
、、、およびに対応します。i
ñ
o
ただし、Railsアプリは、これらのバイトがUTF-8として解釈される必要があると想定し、ある時点でこの方法でデコードしようとします。このシーケンスの3番目のバイトである0xf1は、次のようになります。
1 1 1 1 0 0 0 1
これをウィキペディアページの表と比較すると、このバイトは4バイト文字の先頭バイト(パターンに一致11110xxx
)であり、その後にすべてパターンに一致する3つの継続バイトが続く必要があることがわかります10xxxxxx
。そうではなく、代わりに次のバイトは0x6f(01101111
)であるため、これは無効なutf-8バイトシーケンスであり、表示されるエラーが発生します。
使用:
string = message.encode('utf-8', 'iso-8859-1')
(またはIconv
同等のもの)は、Rubyにmessage
ISO-8859-1エンコードとして読み取り、UTF-8エンコードで同等の文字列を作成するように指示します。これにより、問題なく使用できます。(別の方法として、Rubyに文字列の正しいエンコーディングを指示することもできますがforce_encoding
、後でUTF-8文字列とISO-8859-1文字列を混在させようとすると問題が発生する可能性があります)。
UTF-8では、文字列「niño」はバイトに対応します。
0x6e 0x69 0xc3 0xb1 0x6f
最初、2番目、最後のバイトは同じであることに注意してください。文字はñ
2バイトとしてエンコードされます0xc3 0xb1
。これらをバイナリで書き出して、ウィキペディアの記事の表と比較すると、ISO-8859-1エンコーディングである0xf1がエンコードされていることがわかりますñ
(最初の256個のUnicodeコードポイントはISO-8859-1と一致するため)。
これらの5バイトを取得し、ISO-8859-1として扱う場合、これらは文字列に対応します。
niño
ISO-8859-1コードページを見ると、0xc3はにマップされÂ
、0xb1はにマップされ±
ます。
つまり、ローカルマシンで起こっていることは、アプリが0x6e 0x69 0xc3 0xb1 0x6f
Redisから5バイトを受信していることです。これは、「niño」のUTF-8表現です。0x6e 0x69 0xf1 0x6f
Herokuでは、ISO-8859-1表現である4バイトを受信しています。
問題の本当の解決策は、Redisに入れられる文字列がすべてすでにUTF-8(または少なくともすべて同じエンコーディング)であることを確認することです。私はRedisを使用していませんが、簡単なGoogleからわかるように、文字列エンコーディングには関係なく、与えられたバイトを返すだけです。データをRedisに配置しているプロセスを確認し、エンコードが適切に処理されることを確認する必要があります。