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

luc*_*nte 6 ruby string encoding

我对一些红宝石行为感到困惑.看下面的代码:

[127].pack("C") == "\x7f"   # => true
Run Code Online (Sandbox Code Playgroud)

这是有道理的.现在:

[128].pack("C")             # => "\x80"
"\x80"                      # => "\x80"
[128].pack("C") == "\x80"   # => false
Run Code Online (Sandbox Code Playgroud)

组选"C"表示8-bit unsigned (unsigned char),这应该是罚款存储值128.两个字符串也打印相同的东西,为什么它们不相等?这与编码内容有关吗?

我在ruby 2.0.0p247上.

tes*_*ssi 5

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

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

(使用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的,因为代码点0127ASCII和UTF-8没有区别.这是通过ruby的字符串比较来考虑的(看看rubinius源代码):

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
Run Code Online (Sandbox Code Playgroud)

MRI c源极相似,但更难理解.

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

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

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