在字符编码这个问题上,Ruby1.9相比1.8版本有了大幅变化,本文针对的是Ruby1.9版本,主要参考了这篇文章。
Ruby有三种默认的encodings设置。
###Source Encoding
Ruby的程序源文件可以使用多种字符集编码来编写,所以你必须让Ruby解释器知道你的源文件使用的是哪种字符集编码。请看以下这段代码:
1 | $ cat no_encoding.rb |
从中我们可以知道:
我们可以使用”__ENCODING__“关键字来得到当前正在运行的源程序的字符编码;
我们可以使用”magic comment”来指定源程序的字符编码。如果你的源程序的第一行是注释,并且包含了”coding”这个单词,其后跟着一个分号和一个空格,然后是一个字符编码(encoding)名称,那么这个字符编码名称就被用来指定当前Ruby源程序的字符编码,这一行被成为”魔术注释(magic comment)”;
当你使用交互的命令行方式运行Ruby,则启动Ruby时给定的”-e”开关被用来指定当前运行的Ruby源程序字符编码,即:你在命令行中输入的Ruby程序行会被Ruby解释器用该字符编码来读取。如果你在启动Ruby的命令行中没有指定”-e”开关,则Ruby解释器会读取操作系统环境变量”LC_CTYPE”或”LANG”来作为读取命令行内容时使用的字符编码。
“-KU”参数是为了保持Ruby1.8的兼容性所使用的参数开关,在Ruby1.9中应避免使用。使用”magic comment”是推荐的方式。
“magic comment”的格式相当宽松,以下写法都是合法的:
1 | # encoding: UTF-8 |
###IO Encodings: External and Internal Encodings
很多情况下,String对象是根据从IO对象读入的内容创建的。这时的String字符编码和当前的source encoding无关,而与IO有关。此时程序员确有必要知道应以何种字符编码正确读写IO内容。为此,Ruby1.9引入了external encoding和internal encoding的概念。看以下两段代码:
1 | $ cat show_external.rb |
1 | $ cat show_internal.rb |
从代码中可以看出:
读取文件时,可以显式的指定external encoding,方法是调用open方法时加入read参数”open(__FILE__, “r:UTF-8”)”;
如果读取文件时不指定internal encoding,则File的internal encoding对象默认为nil。这种情况下读取的String对象会默认继承并使用external encoding来创建;
在第二个例子中,调用open方法同时指定了external和internal encodings: “open(__FILE__, “r:UTF-8:UTF-16LE”)”。
在显式指定File对象的interal encoding之后,保存File内容的String对象使用File的internal encoding来创建。
在写文件的时候,情况也类似:Ruby会使用open方法指定的external encoding来保存文本文件。在写文件的情况下,不需要指定internal encoding,因为此时你的String对象已经有了自己的encoding,Ruby只需使用它的encoding作为internal encoding即可。具体请看下面的例子。另外,无论是读还是写,当不同字符编码之前需要转换时,Ruby会自动做这一动作。
1 | $ cat write_internal.rb |
由上述代码可知,IO Encodings的概念比较直接。剩下的问题是:默认情况下会使用什么encoding?
默认的external encoding会来自你的环境变量”LC_CTYPE”或”LANG”,默认的internal encoding为nil。你可以使用以下这两个全局方法修改它们:”Encoding.default_external=()”和”Encoding.default_internal=()”。你还可以在命令行启动Ruby的时候使用开关:
1 | $ ruby -e 'p [Encoding.default_external, Encoding.default_internal]' |
可以看出,这里的命令行开关参数,和File.open()方法参数一致。
Ruby的”-U”命令行开关,会将”Encoding.default_internal()”设置为”UTF-8”,这样,在打开File这种IO对象时,你只需关心”external encoding”,而”internal encoding”统一使用”UTF-8”。
最后要强调,”Encoding.default_external=()”和”Encoding.default_internal=()”这两个方法会影响全局的IO操作,所以尽量少用慎用或不用,相反,在打开每个IO对象的时候指定IO encodings是比较好的实现方式。