我有一个Rails应用程序接受来自第三方来源的JSON数据,我相信我正在遇到一些ActiveRecord幕后魔术,它正在识别从JSON中提取的哈希中的ASCII-8BIT字符并保存它们无论我做什么,都是这样的。
以下是班级的简要描述......
class MyClass < ActiveRecord::Base
serialize :data
end
以及如何创建对象......
a = MyClass.new
a.data = {
"a" =>
{
"b" => "bb",
"c" => "GIF89a\x01\x00\x01\x00\x00\x00\x00\x00!\vNETSCAPE2.0\x03\x01\x00\x00!\x04\t\x00\x00\x01\x00,\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02L\x01\x00;"
}
}
我相信这些是ASCII-8BIT字符,如果它们被保存的话就足够公平(尽管我尝试过无处不在的UTF8)。但是我需要这些字符是UTF-8,因为当我去查看它们时,我得到:
ActionView::Template::Error ("\xEF" from ASCII-8BIT to UTF-8):
64: <div>
65: <pre><%= mc.prettify %></pre>
66: </div>
app/models/my_class.rb:28:in `prettify'
prettify
中的第28行是:
JSON.pretty_generate(self.data)
所以我试图重新编码哈希中的任何字符串。我构建了执行此操作的功能(anonymous classes和refinements到Hash
,Array
和String
),但无论如何,都会返回ASCII-8BIT对我来说。最简单的说法就是发生了什么:
mc = MyClass.find(123)
mc.data['a']['c'].encode!(Encoding.find('UTF-8'), {invalid: :replace, undef: :replace, replace: ''})
mc.data['a']['c'].encoding #=> #<Encoding:UTF-8>
mc.data['a']['c'].valid_encoding? #=> true
mc.save!
mc.reload
mc.data['a']['c'].encoding #=> #<Encoding:ASCII-8BIT> <-- !!!!!
ActiveRecord在保存时对此哈希做了什么?在serialize
d MySQL(v5.6,通过mysql2 gem)mediumtext列(使用Ruby 2.2.4和Rails 4.1.4)中,我可以做什么来永久存储所有编码为UTF-8的字符串的哈希值)?
的my.cnf
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
# ...
skip-character-set-client-handshake
collation_server=utf8_unicode_ci
init-connect='SET NAMES utf8mb4'
character-set-server=utf8
答案 0 :(得分:2)
所以,实际上并不存在“ASCII-8BIT”字符。 ASCII-8BIT
对ruby本质上意味着“根本不编码” - 只是字节,而不假设任何编码。它是'BINARY'的同义词。
但是如果你的字节不是有效的UTF-8,它们实际上不能编码为UTF-8。即使字符串上的编码是UTF-8,当您尝试对其执行某些操作时,最多会出现大量InvalidEncoding错误。
字符串最终标记为什么编码取决于ActiveRecord与数据库本身之间的复杂舞蹈 - 同样,数据库本身有时可能更改您的字节,具体取决于数据库和方式它的设置和你正在做的事情。我们可以尝试准确调试您正在做的事情。
但实际上,答案是 - 如果你想要它是UTF-8,它就不能有二进制非UTF8数据。 “ASCII-8BIT”实际上是二进制数据的正确编码。你究竟想做什么,这些奇怪的字节来自哪里,为什么你想要它们?一般来说,我不确定在JSON中放置任意非UTF8字节是否合法?对于JSON来说它可能是合法的,但它可能会让你头疼(比如你正在处理的那个),因为它取决于两个rails和你的底层数据库究竟要用它们做什么。
为了解决您的显示错误,您可以使用prettify
方法使用scrub
,在ruby 2.1.0中添加以消除当前编码的错误字节。 value.force_encoding("UTF-8").scrub
。这可能会解决你的错误,也许会做正确的事情,但最好弄清楚到底是什么,为什么你想要那些奇怪的字节,它们是什么意味着什么目的。