【问题标题】:Ruby character encoding when using Base64.encode使用 Base64.encode 时的 Ruby 字符编码
【发布时间】:2013-05-11 20:04:42
【问题描述】:

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

【问题讨论】:

    标签: ruby character-encoding base64


    【解决方案1】:

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

    【讨论】:

    • 这与我对维克多答案的怀疑一致。感谢您的确认。
    【解决方案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>
    

    【讨论】:

    • 有趣。从 decode64 返回的字符串是 US-ASCII 并且包含一堆转义字符。我想这是一个很好的指标,它在 base64 编码之前将其转换为 US-ASCII。
    • 如果您想在 irb 会话中试用 Victor 的代码,请确保您先使用 require 'base64'
    • 你刚刚拯救了我的夜晚!谢谢
    猜你喜欢
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-19
    • 2021-06-09
    • 2011-02-06
    • 2012-10-15
    • 2018-02-24
    相关资源
    最近更新 更多