Преобразуйте массив байтов в любую основу

У меня есть массив байтов (любая длина), и я хочу закодировать этот массив в строку с помощью моего собственного основного кодера. В .NET является стандартным Base64 кодер, но что, если я хочу закодировать массив в Base62, Base53 или Base13?

Даже возможно создать такой универсальный основной кодер?

Я знаю, что мог сделать это простой путь, то есть, для каждого резервного постоянного числа байта символов (в случае Base62, это было бы 5 символами), и действительно направьте байт-> кодирование символов, но я потратил бы впустую пространство, как 5 Base62 символы могут содержать больше чем 1 байт, но меньше чем 2 байта.

Как я должен записать такой кодер? Или уже есть ли некоторый класс для этого?
И обратите внимание на то, что мне нужен универсальный декодер также, иначе это бесполезно мне.

Ресурсы

Как решение уже известно (использование BigInteger), я был бы точно так же, как для помещения здесь некоторых ресурсов, имеющих отношение BigInteger класс, поскольку это не доступно в.NET 3.5:

Большие целые числа в C#
http://intx.codeplex.com/
https://svn.apache.org/repos/asf/incubator/heraldry/libraries/csharp/openid/trunk/Mono/Mono. Math/BigInteger.cs
http://www.codeproject.com/KB/cs/BigInteger_Library.aspx
http://www.codeproject.com/KB/cs/biginteger.aspx

12
задан Community 23 May 2017 в 12:17
поделиться

5 ответов

Если производительность не является проблемой, используйте класс BigInteger в фоновом режиме. У вас есть конструктор для BigInteger, который принимает массив байтов, и затем вы можете вручную запускать циклы деления и модуля, чтобы получить представление в других нестандартных базах.

Также посмотрите это .

3
ответ дан 2 December 2019 в 18:51
поделиться

Немного поздно для вечеринки, но ...

Поскольку ваша спецификация требует произвольного количества бит, у вас должен быть целочисленный тип, который может работать с произвольным количеством битов. . Если вы не можете настроить таргетинг на .NET 4.0, вам придется выпросить, позаимствовать или украсть реализацию BigInteger где-нибудь (например, .NET 4.0).

public static class GenericBaseConverter
{
    public static string ConvertToString(byte[] valueAsArray, string digits, int pad)
    {
        if (digits == null)
            throw new ArgumentNullException("digits");
        if (digits.Length < 2)
            throw new ArgumentOutOfRangeException("digits", "Expected string with at least two digits");

        BigInteger value = new BigInteger(valueAsArray);
        bool isNeg = value < 0;
        value = isNeg ? -value : value;

        StringBuilder sb = new StringBuilder(pad + (isNeg ? 1 : 0));

        do
        {
            BigInteger rem;
            value = BigInteger.DivRem(value, digits.Length, out rem);
            sb.Append(digits[(int)rem]);
        } while (value > 0);

        // pad it
        if (sb.Length < pad)
            sb.Append(digits[0], pad - sb.Length);

        // if the number is negative, add the sign.
        if (isNeg)
            sb.Append('-');

        // reverse it
        for (int i = 0, j = sb.Length - 1; i < j; i++, j--)
        {
            char t = sb[i];
            sb[i] = sb[j];
            sb[j] = t;
        }

        return sb.ToString();

    }

    public static BigInteger ConvertFromString(string s, string digits)
    {
        BigInteger result;

        switch (Parse(s, digits, out result))
        {
            case ParseCode.FormatError:
                throw new FormatException("Input string was not in the correct format.");
            case ParseCode.NullString:
                throw new ArgumentNullException("s");
            case ParseCode.NullDigits:
                throw new ArgumentNullException("digits");
            case ParseCode.InsufficientDigits:
                throw new ArgumentOutOfRangeException("digits", "Expected string with at least two digits");
            case ParseCode.Overflow:
                throw new OverflowException();
        }

        return result;
    }

    public static bool TryConvertFromString(string s, string digits, out BigInteger result)
    {
        return Parse(s, digits, out result) == ParseCode.Success;
    }

    private enum ParseCode
    {
        Success,
        NullString,
        NullDigits,
        InsufficientDigits,
        Overflow,
        FormatError,
    }

    private static ParseCode Parse(string s, string digits, out BigInteger result)
    {
        result = 0;

        if (s == null)
            return ParseCode.NullString;
        if (digits == null)
            return ParseCode.NullDigits;
        if (digits.Length < 2)
            return ParseCode.InsufficientDigits;

        // skip leading white space
        int i = 0;
        while (i < s.Length && Char.IsWhiteSpace(s[i]))
            ++i;
        if (i >= s.Length)
            return ParseCode.FormatError;

        // get the sign if it's there.
        BigInteger sign = 1;
        if (s[i] == '+')
            ++i;
        else if (s[i] == '-')
        {
            ++i;
            sign = -1;
        }

        // Make sure there's at least one digit
        if (i >= s.Length)
            return ParseCode.FormatError;


        // Parse the digits.
        while (i < s.Length)
        {
            int n = digits.IndexOf(s[i]);
            if (n < 0)
                return ParseCode.FormatError;
            BigInteger oldResult = result;
            result = unchecked((result * digits.Length) + n);
            if (result < oldResult)
                return ParseCode.Overflow;

            ++i;
        }

        // skip trailing white space
        while (i < s.Length && Char.IsWhiteSpace(s[i]))
            ++i;

        // and make sure there's nothing else.
        if (i < s.Length)
            return ParseCode.FormatError;

        if (sign < 0)
            result = -result;

        return ParseCode.Success;
    }
}
11
ответ дан 2 December 2019 в 18:51
поделиться

BASE64 работает хорошо, потому что 64 - это сила 2 (2^6), поэтому каждый символ содержит 6 бит данных, и 3 байта (3 * 8 = 24 бита) могут быть закодированы в 4 символа (4 * 6 = 24). Кодирование и декодирование может быть сведено к простому сдвигу битов.

Для баз, которые не совпадают с силой 2 (например, база 62 или база 53), вы должны рассматривать сообщение, которое вы пытаетесь закодировать, как одно длинное число и выполнять над ним операции деления и модуляции. Вероятно, лучше использовать кодировку Base32 и потратить немного пропускной способности.

1
ответ дан 2 December 2019 в 18:51
поделиться

Другой пример, на который стоит обратить внимание, - это Ascii85 , используемый в документах Adobe PostScript и PDF. В Ascii85 5 символов используются для кодирования 4 байтов. Вы можете определить эффективность этого кодирования как (256 ^ 4) / (85 ^ 5) = 96,8%. Это часть битовых комбинаций, которые будут фактически использоваться.

Итак, для любой новой базы, которую вы хотели бы использовать для кодирования своих данных, вам нужно искать мощность, которая получит ее чуть выше 256, если вы пытаетесь максимизировать эффективность кодирования. Это может быть нелегко для каждой базы. Проверка базы 53 показывает, что лучшее, что вы, вероятно, получите, - это использовать 7 байтов для кодирования 5 байтов (эффективность 93,6%), если вы не хотите использовать 88 байтов для кодирования 63 байтов.

0
ответ дан 2 December 2019 в 18:51
поделиться

Вы можете черпать вдохновение из C# реализации Base32 Майкла Джагнокаво.

1
ответ дан 2 December 2019 в 18:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: