Ruby Ruby2.0+ 的内部编码设计,以及 Windows 的问题

kiol · 2013年08月14日 · 最后由 kiol 回复于 2013年08月23日 · 5456 次阅读

最近想把一个 1.8.7 上做的一个内部测试框架,转到 ruby2.0 上。因为是在 window 上用的,而我们的代码都是 utf-8 编码编写的,在 windows 上的 ruby1.8.7 编码很多不能满足我们要求的地方,我们做了很多 hack 操作,比如在文件路径做一下 utf-8 到 gbk 的转码等等。

现在 ruby2.0 来了,而且编码上有了很大的变化,而且据说 windows 上启动速度也快很多,所以就动了升级的念头。过程中碰到了很多的问题,通过修改 ruby 的底层 C 代码解决了一些问题,也给社区提交了一些 bug 和补丁,但是和 ruby 开发团队的交流有点儿问题,谁让咱英语不过关呢。所以把自己的一些收获和问题发出来,希望大家能一起交流一下。

现在主要修改的是 Ruby 的 trunk 版,所以以下的 ruby 指的都是 trunk 版。所有问题也是在 windows 下出现的。

ruby 内部有四个 encoding,internal encoding,external encoding,locale encoding,filesystem encoding,好像还有一个 src 的 encoding,我没怎么接触到,默认好像是 utf-8。 一般我们能够指定的是内部和外部编码,用-E 参数可以指定。

我的理解是:

  • internal encoding 是内部的编码,默认情况下 ruby 代码的字符串都应该编码成这个编码。
  • external encoding 是外部的编码,从外部加载进入的内容大多是这个编码。
  • locale encoding 是本地 locale 的编码,和 external encoding 有一定关系,如果没有指定 external encoding 的话,应该默认就是这个编码(我没有确认)
  • filesystem encoding 是文件系统的编码,例如打开文件,枚举目录都返回的内容的编码。

locale encoding 和 filesystem encoding 都是内部实现使用的,如不要代码中一般用不到。

现在的问题是,ruby 内部代码中并没有在所有的边界的地方把编码转换成 internal encoding,比如$LOADPATH 是 locale encoding。导致直接 require 中文文件会编码不兼容错误。

好像 ruby 内部实现并不是按照我说的这个编码原则,我也不知道 ruby 编码原则是啥,也找不到相关的文档,不知道大家有没有相关的信息,或者有知道的也一起讨论一下。

locale encoding 应该是对应 LC_* 设置?windows 没有. external encoding 主要处理 IO 的自动转换,但是可以在 open:encoding 参数来覆盖默认的编码设置. filesystem encoding 主要用来对付文件名. src encoding 是 internal encoding, 主要是字符串/正则字面量的编码,设置 #coding: binary 的话直接在源文件写 gbk 的字符串也是可以的 (有时要手动 force_encoding 一下).

ruby 不统一内码的,字符串自带 encoding 信息,根据你的需求选择编码,可以减少很多不必要的转换

#1 楼 @luikore Windows 不需要吧。都用 W 函数

#2 楼 @bhuztez 表面上看来没有转换,实际上系统在读写文件时悄悄做了转换... 用 ruby 可以全程无编码转换的处理 gbk 文本的

windows 下如果用全程 gbk 的话是很爽,但是我们希望用 utf-8 写代码,就遇到很多问题

#1 楼 @luikore locale 在 Windows 下是基于 ConsoleCP,这个会导致一定的问题。sublime 调用时,ConsoleCP 会是 utf-8 的 codepage 而不是 GBK 的 codepage,但是传的文件名参数又是 GBK 编码的,所以会导致不能执行中文文件名的 rb 文件,没找到 sublime 的改动方法,就直接把 ruby 里面写死成 GBK 了。sublime2 好像可以改,我用的是 3.

#5 楼 @kiol -_- sublime 啊... 大概是 python 的问题...

其实内外统一编码的话,一般不会有啥问题,但是如果不统一的话,现在 ruby 内部实现,我看不到清晰的原则。真想自己 fork 一份改一下

#2 楼 @bhuztez ruby 内部一部分使用带 W 的 api,一部分没有,这也是比较郁闷的:(。

没什么人关心啊,大家是不在 windows 下用吗?还是都是做 rails 的,很少和本地系统打交道啊?

你的源代码 xxx.rb 里面都这样写:

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

当然 xxx.rb 本身也是 utf-8 存储。(用记事本打开,另存为时显示的是 UTF-8,或找个能显示编码的编辑器)

然后你的项目的数据库存储也用 utf-8 . (不然需要 iconv 转换一下,ruby2.0 用 Converter 转换。)

你的项目的配置文件或其他 dat 格式,bin 格式用 utf-8 存储。File.open('xx.dat', "wb:UTF-8")

这样就符合你的需求了。ruby2.0 不用关心文件名是用什么存储的。

#9 楼 @kiol 这是 windows + sublime 特有问题... sublime 不该修改 windows 的 console cp 的,chcp 65001 之后啥都干不了...

在 sublime 设置里加个环境变量试试?RUBYOPTS=-Egbk:utf-8

Ruby 是用 CSI,直来直去的,默认的 internal_encoding 是空的,Ruby2.0 里内部编码和外部编码都是 IO 的编码,可以在程序里指定。-E 参数影响的是默认的外部编码,如果你不指定的话,就用这个默认外部编码,默认内部编码是没有的,也就是你说的,Ruby 本身没有设置 internal_encoding。源文件编码是只和程序的源文件有关的,也就是 1.9 里指定的神奇注释 "#ecoding:utf-8"这种,但是在 Ruby2.0 里已经不需要指定了,默认 utf-8 的。

#10 楼 @sevk 谢谢回复哦。主要希望从底层解决编码问题,这样其他人写代码不再需要指定编码啥的。比如调一个命令行命令的返回值啥都要特出处理的。 而且$LOADPATH 实际上是本地编码的,也就是 GBK 的,如果直接 require‘中文文件“就挂了,因为 ruby 源码里面生成的字符串的编码都是 UTF-8 的,所以和$LOADPATH 整合时就出问题了。

还有中文路径的异常输出的也都没有合理的进行编码的。 我是做一个底层框架,给测试人员使用,测试大量使用中文文件名和中文函数名,我的期望是他们不需要关心编码问题,并且中文的输入输出啥的都没有问题。

#13 楼 @kiol

$LOAD_PATH 里面如果有中文,windows 下面一定是 gbk 的。

如果直接 require‘中文文件“就挂了

解决办法是:


file = '数据.rb'
file.force_encoding('gbk') if win_platform?
require file

#15 楼 @sevk 谢谢,这个方法我知道,我就是觉着很丑。我把 ruby 底层改了,直接返回的就是 UTF-8 编码的$LOADPATH。 还有一个原因是这些代码是要让我们的自动化测试人员写的,每个人写的时候都做这样的 hack,不太好,以后也不好维护。

我个人认为这应该是 ruby 的 bug,我给他们提了个 bug,不过好像没人改。所以说我不知道 ruby 的编码原则到底是什么,这到底算不算 bug。

#11 楼 @luikore 我给 sublime 提了个 bug,http://sublimetext.userecho.com/topic/229067-build-system-can-not-handle-utf-8-args/ 不过可能是 windows 下中文用户比较少吧,没人关注。兄弟帮我顶一下?

不过文件名用中文,感觉是不规范的行为。就像数据库字段名用中文一样,不规范。

而且 NTFS 文件系统,文件名存入硬盘时,其实是 UTF-16 的。

#19 楼 @sevk 你这个是把习惯当成了规范。难道英文比中文高级?英文就规范,中文就不规范。我们用英文是为了和世界接轨和国际的程序员交流。如果交流范围限制在公司内,而且大家英文不是特别好,用中文的好处更多。当然语言和数据库本身的支持是另一个问题。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号