Rails、Heroku和UTF-8无效字节序列错误

10

我有一个 Redis 中的文本消息队列。假设 Redis 中的一条消息如下所示:

"niño" 

(寻找非标准字符)。

这个 Rails 应用程序显示消息队列。当我在本地测试时(Rails 3.2.2,Ruby 1.9.3),一切都很好,但是在 Heroku cedar 上(Rails 3.2.2,我相信是 Ruby 1.9.2),我遇到了臭名昭著的错误:ActionView::Template::Error (invalid byte sequence in UTF-8)

阅读了所有我能在网上找到的东西之后,我仍然无法解决这个问题。

任何帮助或指向正确方向的指引都将不胜感激!

编辑:

我设法找到了解决方法。最终我使用了 Iconv:

string = Iconv.iconv('UTF-8', 'ISO-8859-1', message)[0]

我在网上找到的所有建议答案似乎都不能在我的情况下起作用。


2
当在Ruby 1.9.3中需要Iconv时,你会收到这个警告:iconv将来会被弃用,请使用String#encode代替。 相应的解决方案类似于:string.force_encoding('iso-8859-1').encode('utf-8') - matt
2
或者 string = message.encode('utf-8', 'iso-8859-1') 可能更好。 - matt
好的,谢谢!唯一让我困扰的是,使用我的解决方案后,在我的本地机器(macosx)上,我看到转换后的文本为“niño”,而未转换的文本是正确的“niño”。仍然无法弄清楚原因。 - klaut
你会将 string = Iconv.iconv('UTF-8', 'ISO-8859-1', message)[0] 语句放在哪里? - Muhammed Bhikha
1
我正在对发送到我的应用程序的电子邮件消息进行编码。因此,在将文本放入Redis之前,使用了这行代码。我再次检查了代码(因为自从我最初提出问题以来它已经改变),现在我是这样做的:
  • 首先检查电子邮件的编码方式 email_text_encoding = JSON.parse(params['charsets'])['text']
  • 然后使用该编码方式将其转换为UTF8 utf_ed = text.encode('utf-8', email_text_encoding)
- klaut
显示剩余4条评论
1个回答

37

在Heroku上,当您的应用程序从Redis接收到消息“niño”时,实际上获得的是四个字节:

 0x6e 0x69 0xf1 0x6f

这些字节如果按照ISO-8859-1解释,则对应字符niñ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中的任何过程,并确保它正确处理编码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接