我能否在Ruby 1.9上设置默认的字符串编码?

17

这听起来可能不太重要,但它一直让我很烦恼。自从上周五在Ruby 1.9上发布应用程序以来,我遇到了很多与字符编码相关的小异常。几乎所有的异常都是以下某种变化:

Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and UTF-8
我们有着广泛的国际用户群,因此很多名字都包含umlauts等字符。如果我在一堆地方修复模板以使用force_encoding,那么它就会出现在flash消息助手中,诸如此类。
目前看来,我已经解决了我知道的所有问题,通过在一个地方打补丁修补ActiveSupport的字符串连接,然后在每个源文件的顶部设置# encoding:utf-8 。但是,我必须记得为现在和将来做的每个Ruby项目的每个文件都这样做,以避免字符串赋值问题,这种感觉让我感到不舒服。我读过关于-Ku 开关的内容,但是似乎所有的都警告说它是用于向后兼容的,并且随时可能消失。
因此,我的问题是针对1.9有经验的人:是否真的需要在我的每个文件中设置#encoding?是否有合理的方法可以全局执行此操作?或者更好的方法是在绕过内部/外部默认值的非文字值字符串上设置默认编码?
提前感谢任何建议。
4个回答

13
不要将文件编码与字符串编码混淆。
在文件顶部使用#encoding语句的目的是让Ruby在读取/解释代码时知道如何处理任何非ASCII字符,同时让编辑器知道如何处理这些字符。如果文件中至少有一个非ASCII字符,则必须使用该语句。例如,在config/locale文件中需要使用它。
要一次性为所有文件定义编码,可以使用magic_encoding gem,它可以向应用程序中的所有ruby文件插入uft-8 magic comment。
你在运行时收到的错误Encoding::CompatibilityError是一个错误,当您尝试在程序执行期间连接两个具有不同编码的字符串并且它们的编码不兼容时发生。 这很可能发生在以下情况下:
  • 您正在使用L10N字符串(例如UTF-8),并将它们连接到例如ASCII字符串(在您的视图中)。

  • 用户在外语中键入字符串(例如UTF-8),而您的视图尝试在某个视图中打印它,以及您预定义的一些固定字符串(ASCII)。force_encoding会有所帮助。Rails 1.9中还有Encoding::primary_encoding用于设置新字符串的默认编码。 Rails中的config/application.rb文件中有config.encoding

  • 来自数据库的字符串,然后与视图中的其他字符串组合。(它们的编码方式可能相反且不兼容)。

附注:确保在创建数据库时指定默认编码!

    create database yourproject  DEFAULT CHARACTER SET utf8;

如果你想在字符串中使用EMOJI:

    create database yourproject DEFAULT CHARACTER SET utf8mb4 collate utf8mb4_bin;

所有可能包含表情符号的字符串列上的所有索引需要为191个字符长度。字符集为utf8mb4,排序规则为utf8mb4_bin。
原因是普通的UTF8使用最多3个字节,而表情符号使用4个字节存储。
请查看Yehuda Katz的文章,其中深入涵盖了这一点,并且解释得非常好:(特别是“不兼容编码”部分)。

http://yehudakatz.com/2010/05/05/ruby-1-9-encodings-a-primer-and-the-solution-for-rails/

http://yehudakatz.com/2010/05/17/encodings-unabridged/

并且:

http://zargony.com/2009/07/24/ruby-1-9-and-file-encodings

http://graysoftinc.com/character-encodings


6
我不想处理所有这些编码混乱的问题,知道边缘情况固然好,但我希望没有任何边缘情况。简单地将所有内容视为utf8,并且如果某些内容是其他编码,则必须声明其编码方式。 - grosser
2
@grosser: 我同意 - 这真是一件大烦恼!更糟糕的是,因为它们搞砸了低级 IO 类,原本返回 8 位字节字符串的类,现在返回解释后的“谁知道是什么”的字符串 - 如果你需要处理未解释的原始字节,这就非常烦人。 - Tilo
1
@grosser - 坦白说,在UTF8出现之前,日本必须自己解决问题。由于Ruby在日本的地位以及ISO-2022-JP和Shift_JIS的存在,这就是事实。如果你想成为一个真正的纯粹主义者,仍然有一些字符无法编码成UTF-8。但总体而言,我完全同意你的观点,我们应该都使用UTF8并结束这个话题。 - makdad

6
在你的config/application.rb中添加:
config.encoding = "utf-8"

config/environment.rb文件中,在Application.initialize!代码行上方添加以下两行代码:
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8

希望这能帮到你。

看起来很有希望,但是当加载包含UTF8的'xxx.rb'文件时,仍然会出现相同的旧的多字节错误。 - grosser
据我所知,config.encoding 用于 Rails 的 HTML 输出编码,与 Ruby 的字符串编码无关。 - grosser
这个答案也做出了一个假设(尽管是相当合理的),即OP正在询问关于Rails的问题。 - makdad

3

1
谢谢Trevoke;我知道它们之间的区别。然而,字符串继承了它们创建时所在源文件的编码。(除非它们来自于对另一个文件的IO操作;因此有默认的内部和外部属性。)因此,虽然它们不同,但它们是密切相关且令人沮丧的。我想要的是一种设置默认字符串编码的方法,而不必使用#encoding注释。 - SFEley
1
关于编码,你想知道的一切都在这里: http://blog.grayproductions.net/categories/character_encodings也许还有更多你不想学习的内容 :) - Trevoke

-1
String.module_eval "def initialize\nsuper\nputs encoding\nend"
=> nil
irb(main):006:0> String.new
ASCII-8BIT
=> ""

不确定如何在系统中实现您的字符串,但是通过钩入 String 对象的 initialize 方法,您可以为整个应用程序创建的任何字符串设置编码。


似乎无法修复使用UTF8加载文件的问题。 我尝试了: String.module_eval "def initialize\nsuper\nencoding = Encoding::UTF_8\nend" load 'xxx.rb' - grosser
经过更多的测试,我注意到字符串的初始化方法很少被调用。但这只是一个建议,也许有一个适用于所有字符串的方法,在应用程序中创建它们时调用?只需将编码行添加到其中,而不是初始化。(通过创建,我指加载到内存,解析或其他操作) - kojaktsl
也许覆盖 require 可以解决问题,但我不想走得太远 :D - grosser

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