对于字节数组,比BASE64更紧凑的表示

本文关键字:BASE64 表示 于字节 字节 数组 | 更新日期: 2023-09-27 18:06:16

对于调试,我经常发现将字节数组(例如散列密码)可视化为BASE64字符串很有用。

        public override string ToString()
        {
            return Convert.ToBase64String(this.Hash);      
        }

但是对于大的哈希值(比如超过32字节),BASE64编码产生一个相当长的字符串。这使得仅通过查看它们很难快速比较它们。

BASE64只使用64个可打印字符。我想知道是否有其他编码技术使用超过64个字符(但仍然只是可打印字符)来减少表示32字节所需的长度。在我看来,我们可以大大改进,因为在我的键盘上,我已经看到94个容易区分的可打印键。

当然,使字节数组易于人类比较并不是BASE64最初的目的。但只要有效,对吧?;)

对于字节数组,比BASE64更紧凑的表示

您可以使用Ascii85。维基百科:

Ascii85,也称为Base85,是Paul E. Rutter为btoa实用程序开发的一种二进制到文本的编码形式。通过使用5个ASCII字符来表示4个字节的二进制数据(假设每个ASCII字符8位,编码后的大小比原来的大¹⁄₄),它比使用4个字符来表示3个字节的数据(假设每个ASCII字符8位,增加¹⁄₃)的uuencode或Base64更有效。

你会在github上找到一个c#实现,它是由Jeff Atwood编写的,他在他的博客上附了一篇文章

由于您只需要编码器部分,所以我使用Jeff的代码作为开始,并创建了一个仅包含编码部分的实现:

class Ascii85
{
    private const int _asciiOffset = 33;
    private const int decodedBlockLength = 4;
    private byte[] _encodedBlock = new byte[5];
    private uint _tuple;
    /// <summary>
    /// Encodes binary data into a plaintext ASCII85 format string
    /// </summary>
    /// <param name="ba">binary data to encode</param>
    /// <returns>ASCII85 encoded string</returns>
    public string Encode(byte[] ba)
    {
        StringBuilder sb = new StringBuilder((int)(ba.Length * (_encodedBlock.Length / decodedBlockLength)));
        int count = 0;
        _tuple = 0;
        foreach (byte b in ba)
        {
            if (count >= decodedBlockLength - 1)
            {
                _tuple |= b;
                if (_tuple == 0)
                {
                    sb.Append('z');
                }
                else
                {
                    EncodeBlock(_encodedBlock.Length, sb);
                }
                _tuple = 0;
                count = 0;
            }
            else
            {
                _tuple |= (uint)(b << (24 - (count * 8)));
                count++;
            }
        }
        // if we have some bytes left over at the end..
        if (count > 0)
        {
            EncodeBlock(count + 1, sb);
        }
        return sb.ToString();
    }
    private void EncodeBlock(int count, StringBuilder sb)
    {
        for (int i = _encodedBlock.Length - 1; i >= 0; i--)
        {
            _encodedBlock[i] = (byte)((_tuple % 85) + _asciiOffset);
            _tuple /= 85;
        }
        for (int i = 0; i < count; i++)
        {
            sb.Append((char)_encodedBlock[i]);
        }
    }
}

这里是需要的属性:

/// <summary>
/// adapted from the Jeff Atwood code to only have the encoder
/// 
/// C# implementation of ASCII85 encoding. 
/// Based on C code from http://www.stillhq.com/cgi-bin/cvsweb/ascii85/
/// </summary>
/// <remarks>
/// Jeff Atwood
/// http://www.codinghorror.com/blog/archives/000410.html
/// </remarks>

显示base85如何比base64更紧凑:

将python中的base64转换为base85

import base64
a = "cDINXkoEWkwPIZMJNMyblaL6RY4/8W7edopyZkqop6I="
b = base64.b64decode(a)
c = base64.b85encode(b)
c == b'a54>EN(5R=4<VBYG|ZcoqWVRSKk;tfc8YRlN~ouz'

side by side:

cDINXkoEWkwPIZMJNMyblaL6RY4/8W7edopyZkqop6I=  base64: 44 chars
a54>EN(5R=4<VBYG|ZcoqWVRSKk;tfc8YRlN~ouz      base85: 40 chars

问题?