Ruby支持Unicode吗?它是如何工作的?

32

我刚开始学习Ruby(最终要转向RoR),但有人告诉我Ruby不支持Unicode。这是真的吗?Ruby程序员如何支持Unicode?

5个回答

31
你所听到的内容已经过时,并且仅适用于 Ruby 1.8 或更早版本。最新的稳定版本 Ruby(1.9)支持不少于95种不同的字符编码(在我的系统上计算)。这包括几乎所有已知的 Unicode 转换格式,包括 UTF-8
之前的稳定版本 Ruby(1.8)对 UTF-8 有部分支持
如果你使用 Rails,它会为你处理默认的 UTF-8 编码。如果你只需要 UTF-8 编码意识,Rails 将为你工作,无论你运行 Ruby 1.9 还是 Ruby 1.8。如果你有非常特定的字符编码要求,你应该选择 Ruby 1.9。
如果你真的感兴趣,这里有一系列文章描述了 Ruby 1.8 中的编码问题以及它们是如何被解决的,最终在 Ruby 1.9 中解决。Rails 仍然包括许多常见缺陷在 Ruby 1.8 的解决方法。

对于像我这样寻求编程默认编码切换的 $KCODE 等价快捷方式的人,你需要的是:Encoding.default_internal = 'utf-8' # Encoding.list.map(&:names) - Travis

15

在我的文件顶部添加以下行解决了它。

# encoding: utf-8

14

这不是真的。真正的情况是Ruby不仅支持Unicode,还支持一系列其他编码。

这与Java、.NET或Python等系统形成对比,后者遵循“一种编码规则其它所有”的模型。Ruby有一个设计者称之为“CSI”(Code Set Independent)模型的m17n系统,这意味着每个字符串都被标记了自己的编码,而不是所有字符串都只使用一种编码。

这对于易用性和性能都有一些显著的优势,因为这意味着如果你的输入和输出编码相同,你永远不需要转换编码,而在One True Encoding模型中,你需要在最坏的情况下转换两次(而且这种最坏的情况经常发生,因为大多数这些环境选择了一个实际上没有人使用的内部编码),从输入编码到内部编码,然后再到输出编码。在Ruby中,你最多只需要转换一次编码。

OTE模型的基本问题是,无论你选择哪种编码作为唯一的编码,它都将是完全随意的选择,因为根本不存在一种编码是每个人,甚至大多数人都使用的。

例如,在Java中,他们选择UCS-2作为唯一的编码。然后,几年后,发现UCS-2实际上无法编码所有字符,所以他们不得不对Java进行了一次不兼容的更改,以将UTF-16作为唯一的编码。但当时,世界上很大一部分人已经从UTF-16转向UTF-8了。如果Java在几年前被发明,他们可能会选择ASCII作为唯一的编码。如果是在另一个国家发明,可能会是Shift-JIS。如果是由另一家公司发明,可能会是EBCDIC。这真的完全随意,而如此重要的选择不应该是这样的。


3
@tchrist: 从某种角度来说,Unicode 是一种编码方法,因为它为每个字符分配了一个独特的数字(这基本上是“编码”的字典定义)。但从另一方面来说,Unicode 不是一种将每个字符分配一个唯一的比特模式的编码方式(在 Unicode 的术语中,这是传输格式的工作)。不幸的是,我从来没有想出一个好的名字来描述 Unicode,除了“编码”之外。 - Jörg W Mittag
6
Jörg: [#1] 一个字符集是一组完整的抽象字符。[#2] 编码字符集 将这些抽象字符一一映射到非负整数,称为代码点。[#3] 字符编码函数(或形式)定义了一种精确的位布局,用于对这些整数代码点进行序列化。通过查看比Unicode更小的字符集可能会更容易理解。Radix-50具有50个字符的字符集,并有两种不同的编码字符集(PDP-11之前和之后)。它们的代码点每次打包三个到16位字中。*(续…)* - tchrist
4
按照定义一,Unicode 是一个包含抽象字符的集合,如 LATIN CAPITAL LETTER AE WITH MACRON、GERMAN PENNY SIGN 和 CIRCLED WZ。根据定义二,这三个抽象字符分别被赋予代码点 1E2₁₆、20B0₁₆ 和 1F12E₁₆。按照定义三,这些整数在 UTF-8 下序列化为 "\xC7\xA2"、"\xE2\x82\xB0" 和 "\xF0\x9F\x84\xAE";在 UTF-16BE 下序列化为 "\xE2\x01"、"\xB0\x20" 和 "\x3C\xD8\x2E\xDD";在 UTF‑32LE 下序列化为 "\x00\x00\x01\xE2"、"\x00\x00\x20\xB0" 和 "\x00\x01\xF1\x2E"。对于定义二,我使用“编码”,而对于定义三,我使用“代码点赋值”。明白了吗? - tchrist
1
【更正】Radix-50具有50₈个字符集,相当于40₁₀。 - tchrist
5
@Jörg:你忘了提到Perl。Perl的模型比Java更清晰,因为它使用逻辑代码点(定义#2)而不是Java和Python不明智的序列化代码点(定义#3)。但是,是的,一切都规范化为Unicode字符集(定义#1)。我还没有看到任何合理的证明,说明为什么您希望使用外来的、无法转换成Unicode的代码点,或者永远携带每个字符串的原始序列化信息。我认为这是Ruby的一个严重缺陷,而不是任何可取的特性。这也表明对Unicode的庞大的“私有使用”部分存在误解。 - tchrist
显示剩余3条评论

5

这是一个很老的问题。当前稳定版本的Ruby是2.0.1。是的,它可以处理大多数Unicode字符,但请注意它容易崩溃。

看一下这个代码示例和结果(受这篇文章的启发):

["noël","","baffle"].each do |str|
  puts "Result for '#{str}'"
  puts "  Size: #{str.size}"
  puts "  Reverse: [#{str.reverse}]"
  puts "  Uppercase: [#{str.upcase}]"
end  

Result for 'noël'
  Size: 5 << bad size
  Reverse: [l̈eon] <= accent is shifted
  Uppercase: [NOËL]
Result for ''
  Size: 2
  Reverse: []
  Uppercase: []
Result for 'baffle'
  Size: 4
  Reverse: [efflab] <= doesn't really make sense
  Uppercase: [BAfflE] <= should be "ELFFAB"

重点是:现代 Ruby 处理基础问题 - 不应该依赖更高级的字符串功能。

我没有理解你的评论。为什么efflabbaffle的反转不合理呢?或者为什么baffle的大写应该是ELFFAB - eis
baffle的反转应该是elffab,而不是efflab :-) - kralyk
4
看起来 baffle 被正确对待了,因为 是一个单独的字符。这确实很有道理 :) - ray
现在应该有人为“noël”报告这个错误。 目前,您可以使用gem安装rails,需要active_support/core_ext/string并使用str.mb_chars.reverse - wieczorek1990
我不知道任何需要反转Unicode字符串的用例。你有什么需要在Unicode字符串中进行反转的用例吗? - Eduardo
['a', 'ą', 'b'].sort 在 Ruby 2.3.4 中也会失败(返回 ["a", "b", "ą"] 而不是 ["a", "ą", "b"])。 - reducing activity

0
这个回答中,有人说他们在处理Ruby 1.9中的unicode数据时遇到了Iconv的问题,但我不能保证其准确性。

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