20

查看 Ruby 的 Base64.encode 的源代码,在使用 Base64 编码数据之前,我无法确定字符串转换为何种字符编码(如果有的话)。Base64 编码的 Utf-8 字符串与 Base64 编码的 Utf-16 字符串有很大不同。Ruby 是否对此操作做出任何承诺?

4

2 回答 2

36

在 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>
于 2013-05-16T20:11:46.037 回答
12

精美的手册是这样说的:

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 字符串'µ',那么您最终会将字节0xc20xb5(按此顺序)编码为 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 输出。

于 2013-05-17T00:21:28.017 回答