如何在c#中快速编码然后压缩包含数字的短字符串?

时间:2011-06-15 10:26:40

标签: c#

我的字符串看起来像这样:

000101456890
348324000433
888000033380

它们是长度相同的字符串,只包含数字。

我想找到一种方法来编码然后压缩(减少长度)字符串。压缩算法只需压缩为ASCII字符,因为它们将用作网页链接。

例如:

www.stackoverflow.com/000101456890  goes to www.stackoverflow.com/aJks

我有什么方法可以做到这一点,一些方法可以快速压缩。

谢谢,

2 个答案:

答案 0 :(得分:8)

要做到简单,您可以将每个视为long(那里有足够的空间)和十六进制编码;这给了你:

60c1bfa
5119ba72b1
cec0ed3264

base-64会更短,但你需要将它看作big-endian(注意大多数.NET是little-endian)并忽略前导0字节。这给了你:

Bgwb+g==
URm6crE=
zsDtMmQ=

例如:

    static void Main()
    {
        long x = 000101456890L, y = 348324000433L, z = 888000033380L;

        Console.WriteLine(Convert.ToString(x, 16));
        Console.WriteLine(Convert.ToString(y, 16));
        Console.WriteLine(Convert.ToString(y, 16));

        Console.WriteLine(Pack(x));
        Console.WriteLine(Pack(y));
        Console.WriteLine(Pack(z));

        Console.WriteLine(Convert.ToInt64("60c1bfa", 16).ToString().PadLeft(12, '0'));
        Console.WriteLine(Convert.ToInt64("5119ba72b1", 16).ToString().PadLeft(12, '0'));
        Console.WriteLine(Convert.ToInt64("cec0ed3264", 16).ToString().PadLeft(12, '0'));

        Console.WriteLine(Unpack("Bgwb+g==").ToString().PadLeft(12, '0'));
        Console.WriteLine(Unpack("URm6crE=").ToString().PadLeft(12, '0'));
        Console.WriteLine(Unpack("zsDtMmQ=").ToString().PadLeft(12, '0'));

    }
    static string Pack(long value)
    {
        ulong a = (ulong)value; // make shift easy
        List<byte> bytes = new List<byte>(8);
        while (a != 0)
        {
            bytes.Add((byte)a);
            a >>= 8;
        }
        bytes.Reverse();
        var chunk = bytes.ToArray();
        return Convert.ToBase64String(chunk);
    }
    static long Unpack(string value)
    {
        var chunk = Convert.FromBase64String(value);
        ulong a = 0;
        for (int i = 0; i < chunk.Length; i++)
        {
            a <<= 8;
            a |= chunk[i];
        }
        return (long)a;
    }

答案 1 :(得分:2)

我不确定Base 64是否安全,因为它的索引表中有'/'(所选答案中提供的pack函数将产生非url-safe字符串)。

您可以考虑使用更友好的url或使用其他基础替换'/'符号。例如,Base 62将在这里完成。

这是一个通用代码,可以从十进制来回转换为任何数字基数&lt; = 64(它可能更快然后转换为字节然后使用Convert.ToBase64String()):

static void Main()
{
    Console.WriteLine(Decode("101456890", 10));
    Console.WriteLine(Encode(101456890, 62));
    Console.WriteLine(Decode("6rhZS", 62));
    //Result:
    //101456890
    //6rhZS
    //101456890
}

public static long Decode(string str, int baze)
{
    long result = 0;
    int place = 1;
    for (int i = 0; i < str.Length; ++i)
    {
        result += Value(str[str.Length - 1 - i]) * place;
        place *= baze;
    }

    return result;
}

public static string Encode(long val, int baze)
{
    var buffer = new char[64];
    int place = 0;
    long q = val;
    do
    {
        buffer[place++] = Symbol(q % baze);
        q = q / baze;
    }
    while (q > 0);

    Array.Reverse(buffer, 0, place);
    return new string(buffer, 0, place);
}

public static long Value(char c)
{
    if (c == '+') return 62;
    if (c == '/') return 63;
    if (c < '0') throw new ArgumentOutOfRangeException("c");
    if (c < ':') return c - '0';
    if (c < 'A') throw new ArgumentOutOfRangeException("c");
    if (c < '[') return c - 'A' + 10;
    if (c < 'a') throw new ArgumentOutOfRangeException("c");
    if (c < '{') return c - 'a' + 36;
    throw new ArgumentOutOfRangeException("c");
}

public static char Symbol(long i)
{
    if (i < 0) throw new ArgumentOutOfRangeException("i");
    if (i < 10) return (char)('0' + i);
    if (i < 36) return (char)('A' + i - 10);
    if (i < 62) return (char)('a' + i - 36);
    if (i == 62) return '+';
    if (i == 63) return '/';
    throw new ArgumentOutOfRangeException("i");
}