在Heroku上,当您的应用程序从Redis接收到消息“niño”时,实际上获得的是四个字节:
0x6e 0x69 0xf1 0x6f
这些字节如果按照ISO-8859-1解释,则对应字符n
、i
、ñ
和o
。
但是,您的Rails应用假定这些字节应该被解释为UTF-8,并且在某个时候尝试以这种方式对它们进行解码。这个序列中的第三个字节0xf1看起来像这样:
1 1 1 1 0 0 0 1
如果您将此与维基百科页面上的表进行比较,您会发现这个字节是四字节字符的前导字节(它匹配模式11110xxx
),因此应该跟随三个更多的连续字节,所有这些字节都匹配模式10xxxxxx
。但事实并非如此,接下来的字节是0x6f(01101111
),因此这是无效的utf-8字节序列,您会看到所见错误。
使用:
string = message.encode('utf-8', 'iso-8859-1')
(或者使用Iconv
等价物) 告诉Ruby以ISO-8859-1编码方式读取message
,然后创建相应的UTF-8编码字符串,您可以毫无问题地使用它。(另一种选择是使用force_encoding
来告诉Ruby字符串的正确编码方式,但当您尝试混合使用UTF-8和ISO-8859-1字符串时,这可能会导致问题)。
在UTF-8中,字符串"niño"对应的字节为:
0x6e 0x69 0xc3 0xb1 0x6f
注意第一个、第二个和最后一个字节是相同的。字符“ñ”编码为两个字节“0xc3 0xb1”。如果你将它们写成二进制形式并与维基百科的表格进行比较,你会发现它们编码为0xf1,这是ISO-8859-1编码的“ñ”(因为前256个Unicode代码点与ISO-8859-1匹配)。将这五个字节作为ISO-8859-1处理,则对应于字符串。
niño
根据ISO-8859-1代码页,0xc3映射到Â
,0xb1映射到±
。
因此,在您的本地机器上发生的情况是,您的应用程序从Redis接收五个字节0x6e 0x69 0xc3 0xb1 0x6f
,这是“niño”的UTF-8表示。在Heroku上,它接收四个字节0x6e 0x69 0xf1 0x6f
,这是ISO-8859-1表示形式。
真正解决问题的方法是确保放入Redis中的字符串已经是UTF-8编码(或至少是相同的编码)。我没有使用过Redis,但从我通过简短的Google了解到的情况来看,它并不关心字符串编码,只是返回它所接收到的任何字节。您应该查看将数据放入Redis中的任何过程,并确保它正确处理编码。
iconv将来会被弃用,请使用String#encode代替。
相应的解决方案类似于:string.force_encoding('iso-8859-1').encode('utf-8')
。 - mattstring = message.encode('utf-8', 'iso-8859-1')
可能更好。 - mattstring = Iconv.iconv('UTF-8', 'ISO-8859-1', message)[0]
语句放在哪里? - Muhammed Bhikhaemail_text_encoding = JSON.parse(params['charsets'])['text']
utf_ed = text.encode('utf-8', email_text_encoding)