Ruby 1.9强大的编码问题
如果你用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 => #<Encoding:UTF-8>
而编码问题一般发生在字符串连接时,让我们践行伟大的无产阶级革命精神创造麻烦吧
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 => #<Encoding:UTF-8> 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 defined?(:<<) 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 defined?(: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 defined?(: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
另外还有Encodings, Unabridged和Ruby 1.9 Encodings: A Primer and the Solution for Rails
本文基于 署名-非商业性使用-禁止演绎 2.5 中国大陆 发布
10 COMMENTS >>LEAVE<<
-
ruby 的这个end 看着有点怪不协调
-
@wliment
一般情况还是很协调的,不过现在是因为深深的一层里面只一个方法。。
-
学ruby ,python 有冲突吧 有时间想看看 ,又怕学乱了
-
@wliment
能有什么冲突,放心的学吧。。。
-
额,我也是折腾了好几天才会的
-
m17n最早是用在emacs的邮件客户端,支援国际化比较好的,这个MUA也是matz开发的,我想跟开发者的喜好有关,呵呵。
-
被这编码折磨一晚上
比起 那么辛苦地对着翻译工作爬英文网站
楼主辛苦了,汉字太亲切了. -
终于解决了这个问题,谢谢博主。。。
-
thanks!