Подтвердить что ты не робот

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

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

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

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

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

Ресурсы

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

Большие целые числа в С#
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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Поскольку ваша спецификация требует произвольного количества бит, вы должны иметь целочисленный тип, который может работать с произвольным количеством бит. Если вы не можете настроить таргетинг на .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 there.
        BigInteger sign = 1;
        if (s[i] == '+')
            ++i;
        else if (s[i] == '-')
        {
            ++i;
            sign = -1;
        }

        // Make sure there 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 nothing else.
        if (i < s.Length)
            return ParseCode.FormatError;

        if (sign < 0)
            result = -result;

        return ParseCode.Success;
    }
}

Ответ 3

Вот копия моего blog, который, я надеюсь, поможет как (и почему) конвертировать в Base62

В настоящее время я работаю над собственным сокращением URL: konv.es. Чтобы создать кратчайший хэш-код URL-адреса, я использую метод GetHashCode() для строки, а затем преобразую полученное число в базу 62 ([0-9a-zA-Z]). Самое изящное решение, которое я нашел до сих пор, чтобы сделать преобразование (которое также является удобным примером возврата доходности):

public static IEnumerable<char> ToBase62(int number)
    {
        do
        {
            yield return "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"[number % 62];
            number /= 62;

        } while (number > 0);
    }

Дополнительный кредит: повторный фактор как метод расширения

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

Я написал статью в которой описывается решение на Python, которое точно касается вашей проблемы. Я не использовал очень специальные функции Python, чтобы получить решение, которое может быть легко реализовано на других языках. Возможно, вы посмотрите и узнаете, соответствует ли это вашим потребностям.

Ответ 8

Сообщение в CodeReview побудило меня создать класс RadixEncoding, который способен обрабатывать кодирование/декодирование байтового массива в/из строки base-N.

Этот класс можно найти в этом потоке Q & A, а также документацию по (и решениям) для нескольких краевых случаев при работе с BigInteger, endian-ness поддержка и общая производительность класса