打包的Ruby字符串中的奇怪行为

时间:2013-11-14 12:33:35

标签: ruby string encoding

我对一些红宝石行为感到困惑。请查看以下代码:

[127].pack("C") == "\x7f"   # => true

这是有道理的。现在:

[128].pack("C")             # => "\x80"
"\x80"                      # => "\x80"
[128].pack("C") == "\x80"   # => false

pack option "C"代表8-bit unsigned (unsigned char),应该可以存储128的值。两个字符串也打印相同的东西,为什么它们不相等?这与编码有什么关系吗?

我在ruby 2.0.0p247上。

2 个答案:

答案 0 :(得分:5)

这是错误的,因为编码不同:

[128].pack("C").encoding
#=> #<Encoding:ASCII-8BIT>
"\x80".encoding
#=> #<Encoding:UTF-8>

(使用ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux]

在ruby 2.0中,字符串的默认编码是UTF-8,但不知何故pack返回ASCII 8位编码字符串。

为什么[127].pack('C') == "\x79"为真呢?

但是,[127].pack('C') == "\x79"true,因为代码点0127 ASCII和UTF-8没有区别。这可以通过ruby的字符串比较来考虑(看看the rubinius source code):

def ==(other)
  [...]

  return false unless @num_bytes == other.bytesize
  return false unless Encoding.compatible?(self, other)
  return @data.compare_bytes(other.__data__, @num_bytes, other.bytesize) == 0
end

mri c-source类似,但更难理解。

我们观察到,比较检查兼容的编码。我们试试吧:

Encoding.compatible?([127].pack("C"), "\x79") #=> #<Encoding:ASCII-8BIT>
Encoding.compatible?([128].pack("C"), "\x80") #=> nil

我们看到从代码点128开始,即使两个字符串都由相同的字节组成,比较也会返回false

答案 1 :(得分:1)

在Ruby 1.9中,默认的源文件编码为US-ASCII。从Ruby 2.0开始,默认编码已更改为UTF-8。像"\x80"这样的字符串文字总是使用包含它们的源文件的编码进行编码。

但是,[128].pack("C")的编码为ASCII-8BIT

所以[128].pack("C") == "\x80"在Ruby 2.0中是false而在Ruby 1.9中是true

#coding:some_encoding放在源文件的第一行(或者只是在shebang之后)可以更改默认的源代码编码。

#coding:ascii
puts([128].pack("C") == "\x80")

在Ruby 2.0中输出true