Unicode字符串到字节返回unicode字符串不是不变的

时间:2018-03-15 00:06:17

标签: ruby unicode

在Ruby 2.3.3中,我输入以下代码

require 'scanf'

def hex2str(x)
  if x !~ /\A([0-9a-fA-F]{2})+\z/ then return nil; end
  x.scan(/.{2}/).map{|k| k.scanf("%x")[0].chr}.join
end

def str2hex(s); s.bytes.map {|k| "%02x" % k}.join; end

s="ü"
t=hex2str(str2hex(s))

p s
p t
s.bytes
t.bytes

我得到以下输出:

"ü"
"\xC3\xBC"
[195, 188]
[195, 188]

为什么s ≠ hex2str(str2hex(s))即使是s.bytes = hex2str(str2hex(s)).bytes

似乎在某个地方,正在发生某种自动规范化。有办法避免这种情况吗?您能否提供hex2strstr2hex版本,这些版本不会以任何方式干扰字节且满足s = hex2str(str2hex(s))

1 个答案:

答案 0 :(得分:2)

在Ruby中,String对象的实例由一系列字节以及Ruby认为那些字节所在的编码组成。为了使两个字符串相等他们基本上需要两个相同的字节相同的编码(有一些复杂处理“ascii兼容”字符串,但基本上就是这种情况)。< / p>

您可以使用force_encoding更改此编码标记,而无需更改任何字节。

例如,在编码ISO-8859-1中解释的字节0xC0是À,但在ISO-8859-2中它是Ŕ。显然这些不一样,即使它们包含相同的字节:

# Use the optional argument to chr to specify the encoding to
# use when creating the string.
i1 = 0xC0.chr("ISO-8859-1")
i2 = 0xC0.chr("ISO-8859-2")

puts i1.bytes # => 192
puts i2.bytes # => 192

puts i1.encoding # => ISO-8859-1
puts i2.encoding # => ISO-8859-2

puts i1 == i2 # => false

在您的情况下,由于您没有指定在调用chr时要使用的编码,因此Ruby默认使用ASCII-8BIT,这基本上意味着二进制编码。因此,生成的字符串具有不同的编码,Ruby认为它与原始字符串不同。

由于你知道字符串应该是编码,你可以通过在force_encoding join之后添加对hex2str的调用来告诉Ruby编码(这里)我假设原始字符串编码是UTF-8):

x.scan(/.{2}/).map{|k| k.scanf("%x")[0].chr}.join.force_encoding('utf-8')