Ruby字符串的编码
相比较于Java String的编码转换(先将String根据原字符集转化为字节数组,然后再用字节数组根据新字符集拼装成新的字符串),Ruby的字符串编码转化省去了中间字节数组的过程,直接string.encode(new_encoding)
即可。
字符串与字节串
Ruby的字符串存储的都是字节,还有一个编码,根据这个编码对应的字符集来渲染字符。可以通过如下清除字符串的编码信息还原为裸的字节串:
str = '中' #GBK编码
s1 = [str].pack('a*') => "\xD6\xD0"
str = '中'.encode('UTF-8') #转化为UTF-8编码
s2 = [str].pack('a*') => "\xE4\xB8\xAD"
#Array.pack('a*')是用来向ASCII字符串结尾加入\0用的,所以会清除其他字符串的编码信息
s1.encoding.name => "ASCII-8BIT"
现在得到的s1和s2字符串都是ASCII-8BIT的编码(或者说是没有带字符集的裸字节字符串),只要加上一个编码信息,就可以还原为GBK或者UTF-8的字符串,这种方式和Java也比较像了吧。
s1.force_encoding('GBK') => '中'
s2.force_encoding('UTF-8') => '中'
Ruby中的字节数组,官方并没有给String直接的一个API来获取,但可以用过str.unpack('c*')
或者str.unpack('C*')
来获取,前者带符号位,后者不带符号位。
str = '中' #GBK编码
[str].pack('c*') => [-42, -48] [str].pack('C*') => [214, 208]
str = '中'.encode('UTF-8') #转化为UTF-8编码
[str].pack('c*') => [-28, -72, -83] [str].pack('C*') => [228, 184, 173]
UTF-8和Unicode
不清除具体原因,在Ruby 1.9.3中,UTF-8字符串和Uniocde有一种特殊的自动转化方式(低于Ruby 1.9的版本,没有Unicode的支持),会自动进行转化,只要UTF-8字节串给加上了UTF-8的编码信息,字符串就自动转化为Unicode模式了。
"\xE4\xB8\xAD".force_encoding('UTF-8') == "\u4E2D" => true
将Unicode转化为UTF-8字符串:
"\u4E2D".each_byte {|b| print b.to_s(16)} => 'e4b8ad' #可见Unicode底层用UTF-8字节串存储
#字符串格式
"\u4E2D" # 底层就是UTF-8串
#双字节数字格式
0x4E2D(16进制) 或者 20013(10进制) => [0x4E2D].pack('U*') => "\u4E2D"