Rails,Heroku和UTF-8中的无效字节序列错误

kla*_*aut 10 ruby-on-rails heroku utf-8 redis

我在Redis中有一个短信队列.假设redis中的消息是这样的:

"niño" 
Run Code Online (Sandbox Code Playgroud)

(发现非标准字符).

rails app显示消息队列.当我在本地测试时(Rails 3.2.2,Ruby 1.9.3)一切都很好,但是在Heroku雪松上(Rails 3.2.2,我相信有红宝石1.9.2)我得到了臭名昭着的错误:ActionView::Template::Error (invalid byte sequence in UTF-8)

阅读并重新阅读我在网上找到的所有内容后,我仍然坚持如何解决这个问题.

任何帮助或指向正确的方向非常感谢!

编辑:

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

string = Iconv.iconv('UTF-8', 'ISO-8859-1', message)[0]
Run Code Online (Sandbox Code Playgroud)

我发现周围的建议答案似乎都不适用于我的情况.

mat*_*att 37

在Heroku上,当你的应用程序从Redis收到消息"niño"时,它实际上得到了四个字节:

 0x6e 0x69 0xf1 0x6f
Run Code Online (Sandbox Code Playgroud)

当解释为ISO-8859-1对应于字符ni,ño.

但是,您的Rails应用程序假定这些字节应该被解释为UTF-8,并且在某些时候它尝试以这种方式解码它们.这个序列中的第三个字节,0xf1如下所示:

1 1 1 1 0 0 0 1
Run Code Online (Sandbox Code Playgroud)

如果将其与维基百科页面上进行比较,您可以看到此字节是四字节字符的前导字节(它与模式匹配11110xxx),因此应该跟随三个所有匹配模式的连续字节10xxxxxx.它不是,而是下一个字节是0x6f(01101111),所以这是无效的utf-8字节序列,你得到你看到的错误.

使用:

string = message.encode('utf-8', 'iso-8859-1')
Run Code Online (Sandbox Code Playgroud)

(或Iconv等效的)告诉Ruby读取message为ISO-8859-1编码,然后以UTF-8编码创建等效字符串,然后您可以毫无问题地使用它.(另一种方法是使用force_encoding告诉Ruby正确的字符串编码,但是当你尝试混合使用UTF-8和ISO-8859-1字符串时,这可能会导致问题).

在UTF-8中,字符串"niño"对应于字节:

0x6e 0x69 0xc3 0xb1 0x6f
Run Code Online (Sandbox Code Playgroud)

请注意,第一个,第二个和最后一个字节是相同的.的ñ字符被编码为两个字节0xc3 0xb1.如果你用二进制文件写出来并再次与维基百科中的表格进行比较,你会看到它们编码为0xf1,这是ISO-8859-1编码ñ(因为前256个unicode代码点与ISO-8859-1匹配).

如果你取这五个字节并将它们视为ISO-8859-1,那么它们对应于字符串

niño
Run Code Online (Sandbox Code Playgroud)

查看ISO-8859-1代码页,0xc3映射到Â,0xb1映射到±.

所以在你的本地机器上发生的事情是你的应用程序0x6e 0x69 0xc3 0xb1 0x6f从Redis 接收五个字节,这是"niño"的UTF-8表示.在Heroku上,它接收四个字节0x6e 0x69 0xf1 0x6f,即ISO-8859-1表示.

解决问题的真正方法是确保放入Redis的字符串都是UTF-8(或者至少是所有相同的编码).我没有使用过Redis,但是我从简短的Google中可以看出,它并不涉及字符串编码,只是简单地给出了它给出的任何字节.您应该查看将数据放入Redis的任何进程,并确保它正确处理编码.

  • 哇,答案应该是这样的! (2认同)