为什么在Ruby中,7位ASCII字符串文字被编码为UTF-8?

4
我正在阅读《Ruby编程语言》第3.2.6.1节“Ruby 1.9中的多字节字符”,该书介绍了Ruby字符串的一种优化。
如果一个字符串文字只包含7位ASCII字符,则其编码方法将返回ASCII,即使源编码为UTF-8。
我在ruby 1.9.1-p431、1.9.2和1.9.3-p125上尝试了以下简单脚本,它们都将7位ASCII字符解码为UTF-8。
# coding: utf-8
s = 'hello'
p s.encoding
# result is #<Encoding:UTF-8>

我猜可能是在Ruby 1.9的开发过程中改变了这种行为。我试图搜索Ruby 1.9的更改记录,1.9.1 changelog证实了这一点。我也克隆了Ruby的GIT存储库,但找不到提到更改此行为的提交。
更新:
查看Ruby的源代码存储库,我想这就是Ruby 1.9.0中的行为,该版本于2008年1月发布。(它无法在Debian 6上编译,因此我无法完全确认这一点。)虽然《Ruby编程语言》是一本精彩的书,但它最初是在2008年出版的。这本书中的某些描述很可能已经过时了。
另一个过时的描述是关于Encoding.list方法的行为。因此,如果您也正在阅读这本书,请注意过时的描述。

只是好奇:仅包含ASCII字符的7位ASCII和UTF-8编码字符串有何不同? - zrslv
如果您好奇,请使用 s.bytes 进行检查。不过,按定义,7位字符在 UTF-8 中存储时不会改变。 - tadman
如果字符串的编码设置为UTF-8,则使用[]进行索引必须从字符串开头遍历。如果去除了ASCII-8BIT优化,我猜可能会有其他优化来避免这种情况。 - cyfdecyf
2个回答

4
我没有那本书,但是《Programming Ruby》(即pickaxe)当前的PDF版本说明:

字符串字面量始终使用包含它们的源文件的编码进行编码,不考虑字符串的内容

然后给出了一个例子,其中"dog"获得了UTF-8编码。看起来你手头的这个版本有误。无论是在印刷版中发现的勘误,还是因为Ruby在印刷后发生了变化,我都不知道。

也许行为已经改变了。我会尝试下载 Ruby 1.9.1 并再次运行该脚本。 - cyfdecyf

2
需要翻译的内容:

需要注意的是,在 Ruby 中,“编码”通常指的是“解释”,而不只是存储的字节。当字符串的编码为 UTF-8 时,这意味着该字符串中的字节将被解释为 UTF-8 多字节字符。虽然由于 UTF-8 的设计使其向后兼容 7 位 ASCII,因此在二进制级别上并没有明显差异。

Ruby 不会自动检测字符串的编码,因为没有一种标准或可靠的方法来确定它。这就是为什么默认的编码方法适用于所有字符串,除非在创建或转换时显式指定。

您可以使用 force_encoding 切换字符串的编码而不实际修改存储的字节。您还可以使用 encode 转换为不同格式,可能重新映射存储的字节。

如果您想了解字符串的内部信息,有几种方法可供探索:

'dog'.encoding
# => #<Encoding:UTF-8> 
'dog'.bytes.to_a
# => [100, 111, 103] 
'dog'.chars.to_a
# => ["d", "o", "g"]

与非7位ASCII字符串进行比较:
'døg'.encoding
# => #<Encoding:UTF-8> 
'døg'.bytes.to_a
# => [100, 195, 184, 103]
'døg'.chars.to_a
# => ["d", "ø", "g"]

我理解底层字节和将字节解释为字符之间的关系。只是书中的描述与当前Ruby的实现不匹配。查看Ruby源代码显示,它曾经将7位ASCII字符串的编码设置为ASCII-8BIT,可能是在Ruby 1.9.0时期,但我无法在Debian 6上编译该版本。我找不到行为何时改变为当前行为。 - cyfdecyf

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