如果你用Ruby 1.9不会没有遇到过编码的问题吧,Ruby 1.9支持强大的m17n,强大到我一开始接触它都要疯掉了。。。
作为一个好屁民我决定把我的痛苦经历写一写,毕竟我还没见过这方面的中文介绍。Ruby是一种很特别的语言,Ruby 1.9更是个神奇的存在。我是用Ruby 1.9后才知道还有m17n这一说法的,看来我太寡聞了么。
我遇到的错误,相信你也遇到过,除非你都用的ASCII码或者你比Ruby更怪
ActionView::Template::Error (incompatible character encodings: ASCII-8BIT and UTF-8)
伟大的革命精神是怎样的?克服一切困难,没有问题也要制造问题然后解决它。解决问题之前,先来介绍一点Ruby 1.9 encoding的处理方式。
谈到编码,自然就要讲字符串,Ruby 1.9中的字符串不是一个人,他已经被编码附体了!
一个String对象不仅包含着一般意义上的字符串,还有一个Encoding对象与它关联
irb(main):014:0> s = '中文'
=> "中文"
irb(main):015:0> s.encoding
=> #
而编码问题一般发生在字符串连接时,让我们践行伟大的无产阶级革命精神创造麻烦吧
irb(main):026:0> s.force_encoding('BINARY')
=> "/xE4/xB8/xAD/xE6/x96/x87"
irb(main):027:0> x = '也是中文'
=> "也是中文"
irb(main):028:0> x.encoding
=> #
irb(main):029:0> s x
Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and UTF-8
麻烦已经被我们制造出来了,可以看到与上面的错误是一样的,我们要做的就是解决掉它。
首先找到问题代码的位置在action_view/template/handlers/erb.rb,我们写一个initializer覆盖它。
# config/initializers/rails/action_view/template/handlers/erb.rb
require "action_view/template/handlers/erb"
ActionView::OutputBuffer.class_eval do
undef if respond_to?(:)
def (value)
self.force_encoding(Encoding.default_external)
value = value.to_s.force_encoding(Encoding.default_external)
super(value)
end
alias :append= :
end
代码意思很明确,在erb需要拼接时把双方都转化为Encoding.default_external,这个在Rails里默认是UTF-8的。
问题解决了吗?不!革命一定要彻底,把能找到的隐患全部消灭。数据库里读取数据自然不会少了字符串,编码也一定如影随形
# config/initializers/rails/active_record/connection_adapters/sqlite_adapter.rb
require 'active_record/connection_adapters/sqlite_adapter'
module ActiveRecord
module ConnectionAdapters
SQLiteAdapter.class_eval do
undef select if respond_to?(:select)
protected
def select(sql, name = nil)
execute(sql, name).map do |row|
record = {}
row.each do |k, v|
if k.is_a?(String)
v.force_encoding(Encoding.default_external) if v.respond_to?(:force_encoding)
record[k.sub(/^"?/w+"?/./, '')] = v
end
end
record
end
end
end
end
end
我可是sqlite3的坚决拥趸,自然就只顾着sqlite3了。
另一个问题是表单提交的信息,只要有字符串产生的地方就有编码隐患,消灭它
# config/initializers/rails/action_dispatch/http/parameters.rb
require 'action_dispatch/http/parameters'
module ActionDispatch
module Http
module Parameters
undef normalize_parameters if respond_to?(:normalize_parameters)
private
def normalize_parameters(value)
case value
when Hash
h = {}
value.each { |k, v| h[k] = normalize_parameters(v) }
h.with_indifferent_access
when Array
value.map { |e| normalize_parameters(e) }
else
value.force_encoding(Encoding.default_external)
end
end
end
end
end
多谢呼呼的提示,到最后我还是采纳了他的方案,现在问题暂时被解决了。
当然这些作案方式都是些很无耻的写法,如果一切都这么简单,Rails开发小组是不会把问题留给我的,嘻哈。
至于Ruby为什么选用现在的m17n,而不是像其他大部分语言在内部统一的使用一种编码,比如UTF-8。据说是为了方便不同编码的程序员搞基顺利?为了给程序员选择的自由?啊,我宁愿不要这种自由
想深入了解,这是对字符编码和Ruby字符处理的一系列文章Understanding M17n