Handling UTF-8 in Ruby

January 28, 2008 – 3:20 pm

Ruby 1.9.0 已经发布了,1.9 的一个重大改进就是对 Unicode 的支持,这里有一篇介绍 Ruby 1.9 中 Unicode 的文章,可惜是日文的,不过配合代码和部分汉字应该能理解大概意思。

在 Ruby 1.9 中,将如下代码保存为 UTF-8 编码,可以轻松运行通过:

# -*- coding: utf-8 -*-
 
require 'test/unit'
 
class TestUnicode < Test::Unit::TestCase
  def setup
    @str = '这是中文'
  end
 
  def test_size
    assert_equal 4, @str.size
  end
 
  def test_at
    assert_equal '这', @str[0]
  end
 
  def test_reverse
    assert_equal '文中是这', @str.reverse
  end
end

注意第一行的注释中的 coding: utf-8 是必须的(当然也可以通过其他各种方式来指定,不过我很喜欢这种方式,因为这样 Emacs 也可以认出这个文件的编码来)。在 Ruby 1.8 中就没有这么方便的事情了,不过由于 Ruby 1.9 刚刚发布,Ruby 1.9.0 又是“不稳定版本”,许多库还只能工作在 1.8 之下,所以还得有相当长一段时间内需要 Ruby 1.8 的相关 workaround 来处理 Unicode 以及国际化等相关的工作。

在 Ruby 1.8 中,可以使用全局变量 $KCODEjcode 来实现一些常用函数。例如,要使用 UTF-8 编码,首先把 $KCODE 设置为 'u' ,设置之前是这样:

irb(main):001:0> str = "中文"
=> "\344\270\255\346\226\207"
irb(main):002:0> p str
"\344\270\255\346\226\207"
=> nil

设置之后好多了:

irb(main):001:0> $KCODE = 'u'
=> "u"
irb(main):002:0> str = "中文"
=> "中文"
irb(main):003:0> p str
"中文"
=> nil

但是 str.length 还是会返回 6 而不是 2 。要解决这个问题,可以使用 jcode 里的 jlength

irb(main):005:0> require 'jcode'
=> true
irb(main):006:0> str.jlength
=> 2
irb(main):007:0> str.length
=> 6

另外,jcode 中的 each_char 可以正确地遍历每个字符(而不是字节),如果没有传递一个 block 作为参数,它会返回一个字符数组:

irb(main):008:0> str.each_char
=> ["中", "文"]

更多的信息可以参加 jcode文档。其实 jcode 提供的方法并不多,不过查看 jcode 的代码就可以看到,其实使用正则表达式 /./ 就可以正确匹配一个字符,不管是 CJK 字符还是普通的拉丁字母。

不管怎么说,始终还是不如 Ruby 1.9 中完美啊,只有期望 Ruby 1.9 赶快流行起来,大家都赶快迁移到新版本来吧! :D

Post a Comment