12


2

バイト配列を任意のベースに変換します

バイト(任意の長さ)の配列があり、独自のベースエンコーダーを使用してこの配列を文字列にエンコードしたい。 .NET`には標準の Base64`エンコーダーがありますが、配列を Base62、` Base53`または `Base13`にエンコードしたい場合はどうすればよいですか?

そのようなユニバーサルベースエンコーダーを作成することさえ可能ですか?

私はそれを簡単な方法で行うことができることを知っています、つまり、各バイトは固定数の文字を予約し(「Base62」の場合は5文字です)、直接バイト→文字エンコーディングを行いますが、無駄になります5つの「Base62」文字には1バイト以上、2バイト未満を含めることができるため、スペース。

そのようなエンコーダーをどのように書くべきですか? または、このためのクラスがすでにありますか? +そして、私もユニバーサルデコーダーが必要であることに注意してください、そうでなければ、これは私にとって役に立たないです。

リソース

ソリューションはすでに知られているので(「BigInteger」を使用)、. NET 3.5では使用できないため、「BigInteger」クラスに関連するリソースをここに配置します。

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

8 Answer


11


パーティーに少し遅れましたが…​

仕様では任意のビット数を必要とするため、任意のビット数で機能する整数型が必要です。 .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;
    }
}


3


パフォーマンスに問題がない場合は、バックグラウンドでhttp://msdn.microsoft.com/en-us/library/system.numerics.biginteger_members.aspx[BigInteger]クラスを使用します。 バイト配列を受け取るBigIntegerのコンストラクターがあり、その後、手動で除算とモジュラスのループを実行して、他の非標準ベースの表現を取得できます。

また、http://msdn.microsoft.com/en-us/magazine/cc163696.aspx [this]もご覧ください。


3


blogからのコピーは、Base62に変換する方法(および理由)に役立つと思います

現在、自分のURL短縮サービスkonv.esを作成しています。 URLの最短文字ハッシュを作成するために、文字列のGetHashCode()メソッドを使用し、結果の数値をベース62([0-9a-zA-Z])に変換します。 私がこれまでに変換を行うために見つけた最もエレガントな解決策(これは利回りリターンの便利な例です):

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

        } while (number > 0);
    }

追加クレジット:拡張方法としてのリファクタリング


1


Michael Giagnocavoによるhttp://www.atrevido.net/blog/2004/01/13/Base32%2BIn%2BNET.aspx[Base32]実装のC#実装からインスピレーションを得ることができます。


1


BASE64はうまく機能します。64は2のべき乗(2 ^ 6)であるため、各文字は6ビットのデータを保持し、3バイト(3 * 8 = 24ビット)は4文字(4 * 6 = 24)にエンコードできます。 エンコードとデコードは、ビットシフトビットだけにすることができます。

2のべき乗と一致しないベース(ベース62またはベース53など)の場合、エンコードしようとしているメッセージを1つの長い数値として扱い、それに対して除算およびモジュロ演算を実行する必要があります。 おそらく、Base32エンコードを使用し、少し帯域幅を浪費する方が良いでしょう。


0


もう1つの例は、Adobe PostScriptおよびPDFドキュメントで使用されるhttp://en.wikipedia.org/wiki/Ascii85[Ascii85]です。 Ascii85では、4バイトのエンコードに5文字が使用されます。 このコーディングの効率は、(256 ^ 4)/(85 ^ 5)= 96.8%であることがわかります。 これは、実際に使用されるビットの組み合わせの割合です。

したがって、データのエンコードに使用する新しいベースについては、コーディングの効率を最大化しようとする場合、256の累乗をわずかに上回る能力を探したいと思います。 これはすべての拠点にとって簡単ではないかもしれません。 ベース53を確認すると、88バイトを使用して63バイトをエンコードしたいと思わない限り、おそらく7バイトを使用して5バイトをエンコードすることが効率的です(93.6%の効率)。


0


私はhttp://www.artificial-informagic.com/2011/05/01/convert-block-digits-base-x-base-y[article]を書きました。問題。 他の言語で簡単に実装できるソリューションを得るために、Pythonの特別な機能を使用しませんでした。 あなたが見て、それがあなたのニーズに合うかどうかを調べるかもしれません。


0


CodeReviewの投稿は、エンコード/デコードを処理できるRadixEncodingクラスを作成するように促しましたbase-N文字列との間のバイト配列。

クラスはhttps://stackoverflow.com/questions/14110010/base-n-encoding-of-a-byte-array [このQ&Aスレッド]に、いくつかのエッジケースに関するドキュメント(およびソリューション)があります。 BigInteger、エンディアンネスのサポート、およびクラスの全体的なパフォーマンスを扱う場合