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