查看 Ruby 的 Base64.encode 的源代码,在使用 Base64 编码数据之前,我无法确定字符串转换为何种字符编码(如果有的话)。Base64 编码的 Utf-8 字符串与 Base64 编码的 Utf-16 字符串有很大不同。Ruby 是否对此操作做出任何承诺?
2 回答
在 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>
精美的手册是这样说的:
encode64(bin)
返回 bin 的 Base64 编码版本。此方法符合 RFC 2045。
RFC 2045的第 6.8 节说:
6.8. Base64 内容传输编码
Base64 Content-Transfer-Encoding 旨在以不需要人类可读的形式表示任意八位字节序列。[...]
使用 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"
输出。
当您 decode 时"wrU=\n"
,您将获得字节"\xc2\xb5"
并且您必须知道这些字节应该是 UTF-8 编码的文本,而不是一些任意的位块。这就是为什么您将单独的内容类型和字符集元数据附加到 Base64。
同样,如果您有一个 UTF-16 字符串,那么它将被分解为字节,并且这些字节将像任何其他字节字符串一样被编码。当然,由于字节顺序问题,这种情况稍微复杂一些,但这就是我们有内容类型和字符集标题和 BOM 的原因。
要点是 Base64 与字节一起使用,而不是字符。什么格式(UTF-8 文本、UTF-16 文本、PNG 图像……)是别人的问题。Base64 只是将字节流转换为 US ASCII 的子集,然后再转换回字节;这些字节的格式必须单独指定。
我在源代码中进行了一些探索,即使它们不完全相关,结果也可能很有趣。encode64
方法很简单:
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 输出。