【发布时间】:2013-05-11 20:04:42
【问题描述】:
查看 Ruby 的 Base64.encode 的源代码,在使用 Base64 编码该数据之前,我无法确定字符串转换为何种字符编码(如果有的话)。 Base64 编码的 Utf-8 字符串与 Base64 编码的 Utf-16 字符串有很大不同。 Ruby 是否对此操作做出任何承诺?
【问题讨论】:
标签: ruby character-encoding base64
查看 Ruby 的 Base64.encode 的源代码,在使用 Base64 编码该数据之前,我无法确定字符串转换为何种字符编码(如果有的话)。 Base64 编码的 Utf-8 字符串与 Base64 编码的 Utf-16 字符串有很大不同。 Ruby 是否对此操作做出任何承诺?
【问题讨论】:
标签: ruby character-encoding base64
fine manual 有话要说:
encode64(bin)
返回 bin 的 Base64 编码版本。此方法符合 RFC 2045。
RFC 2045 的第 6.8 节说:
6.8。 Base64 内容传输编码
Base64 内容传输编码旨在以不需要人类可读的形式表示任意八位位组序列。 [...]
使用 65 个字符的 US-ASCII 子集,每个可打印字符可以表示 6 位。 (额外的第 65 个字符“=”用于表示特殊处理功能。)
所以 Base64 将 字节 编码为 ASCII。如果这些字节实际上代表一个 UTF-8 编码的字符串,那么 UTF-8 字符串将被分解为单独的字节,这些字节将被转换为 Base64;例如,如果您有一个 UTF-8 字符串 'µ',那么您最终会将字节 0xc2 和 0xb5(按此顺序)编码为 Base64 表示 "wrU=\n"。如果您从二进制字符串 "\xc2\xb5" 开始(恰好与 'µ' 的 UTF-8 版本匹配),那么您将获得相同的 "wrU=\n" 输出。
当您解码"wrU=\n" 时,您将获得字节"\xc2\xb5",并且您必须知道这些字节应该是UTF-8 编码的文本,而不是一些任意的位块。这就是为什么您将单独的内容类型和字符集元数据附加到 Base64。
同样,如果您有一个 UTF-16 字符串,那么它将被分解为字节,并且这些字节将像任何其他字节字符串一样被编码。当然,由于字节顺序问题,这种情况会稍微复杂一些,但这就是我们有内容类型和字符集标头和 BOM 的原因。
重点是 Base64 与 bytes 一起工作,而不是字符。什么格式(UTF-8 文本、UTF-16 文本、PNG 图像……)是别人的问题。 Base64 只是将字节流转换为 US ASCII 的子集,然后再转换回字节;这些字节的格式必须单独指定。
我在源代码中进行了一些探索,结果可能很有趣,即使它们并不完全相关。 encode64 method 就是这样的:
def encode64(bin)
[bin].pack("m")
end
那么如果你翻看Array#pack:
static VALUE
pack_pack(VALUE ary, VALUE fmt)
{
/*...*/
int enc_info = 1; /* 0 - BINARY, 1 - US-ASCII, 2 - UTF-8 */
并密切关注enc_info,您会看到'm' 格式将单独留下enc_info,因此打包字符串将作为US-ASCII 输出,因此encode64 将生成US ASCII 输出为预计。
【讨论】:
一个在base64中编码和解码一个utf-8字符串的例子:
text = "intérnalionálização"
=> "intérnalionálização"
text.encoding
=> #<Encoding:UTF-8>
encoded = Base64.encode64(text)
=> "aW50w6lybmFsaW9uw6FsaXphw6fDo28=\n"
encoded.encoding
=> #<Encoding:US-ASCII>
decoded = Base64.decode64(encode)
=> "int\xC3\xA9rnalion\xC3\xA1liza\xC3\xA7\xC3\xA3o"
decoded.encoding
=> #<Encoding:US-ASCII>
decoded = decoded.force_encoding('UTF-8')
=> "intérnalionálização"
decoded.encoding
=> #<Encoding:UTF-8>
【讨论】:
require 'base64'。