Кратчайший способ представления UInt64 в виде строки

Я получаю возможно большое число (UInt.MaxValue: 18446744073709551615) как обычное число base10. Этот номер в конечном итоге станет именем файла: 12345678945768.txt

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

Для меньших чисел: 0001365555, hexed намного короче всего остального. Все, что я нашел до сих пор, утверждает, что Base64 будет самым коротким, но это не так.

Пока я пробовал это:

//18446744073709551615 - 20
UInt64 i = UInt64.MaxValue; // 0001365555

//"//////////8=" - 12
string encoded = Convert.ToBase64String(BitConverter.GetBytes(i)); 

//"FFFFFFFFFFFFFFFF" - 16
string hexed = i.ToString("X"); 

//"MTg0NDY3NDQwNzM3MDk1NTE2MTU=" - 28
string utf = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(i.ToString())); 

Есть ли лучший способ "сжать" целое число для преобразования, аналогичного Hex, но использовать 00-zz, а не только 00-FF?

Заранее спасибо!

4 ответа

Решение

Все, что я нашел до сих пор, утверждает, что Base64 будет самым коротким, но это не так.

Вы не хотите использовать Base64. Кодированный в Base64 текст может использовать / символ, который запрещен в именах файлов в Windows. Вам нужно придумать что-то еще.

Что-то еще?

Ну, вы могли бы написать свое собственное базовое преобразование, возможно, что-то вроде этого:

public static string Convert(ulong number)
{
    var validCharacters = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890!@#$%^&()_-";
    char[] charArray = validCharacters.ToCharArray();
    var buffer = new StringBuilder();
    var quotient = number;
    ulong remainder;
    while (quotient != 0)
    {
        remainder = quotient % (ulong)charArray.LongLength;
        quotient = quotient / (ulong)charArray.LongLength;
        buffer.Insert(0, charArray[remainder].ToString());
    }
    return buffer.ToString();
}

Это результат "base-73", чем больше символов в validCharactersчем меньше будет выходной. Не стесняйтесь добавлять больше, если они являются допустимыми символами в вашей файловой системе.

Какой у вас разрешенный набор символов? Если бы вы могли идентифицировать 7132 различных символов Юникода, которые можно было бы безопасно использовать, вы могли бы закодировать 64-разрядное число в виде пяти символов Юникода. С другой стороны, не все файловые системы будут поддерживать такие символы. Если бы вы могли идентифицировать 139 допустимых символов, вы могли бы сжать данные в строку из девяти символов. С 85 вы можете использовать десятисимвольную строку.

Вы неправильно использовали Base64.

(System.Text.Encoding.ASCII.GetBytes(i.ToString())

Это создает последовательность байтов, которая содержит закодированное в base10 целое число, и кодирует ее снова в base64. Это явно неэффективно.

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

И вы должны обрезать 0 байтов на одной стороне массива.

var bytes=BitConverter.GetBytes(input);
int len=8;
for(int i=7;i>=0;i--)
{
  if(bytes[i]!=0)
  {
    len=i+1;
    break;
  }
}
string s=Convert.ToBase64String(bytes,0,len).ReplaceString('/','-');

Обратите внимание, что это не будет работать, как ожидается, в системах с прямым порядком байтов.

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

Простая версия может быть:

string digitChars="0123..."
while(i!=0)
{
  int digit=i%digitChars.Length;
  i/=digitChars.Length;
  result=digitChars[digit]+result;
}

Вот код, который использует ответ vcsjones выше, но также включает обратное преобразование. Как и в его ответе, не стесняйтесь добавлять больше символов, если необходимо уменьшить размер строки. Приведенные ниже символы дают размер строки 13 для ulong.MaxValue.

private const string _conversionCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

public static string UlongToCompressedString(ulong number)
{
    char[] charArray = _conversionCharacters.ToCharArray();
    var buffer = new System.Text.StringBuilder();
    var quotient = number;
    ulong remainder;
    while (quotient != 0)
    {
        remainder = quotient % (ulong)charArray.LongLength;
        quotient = quotient / (ulong)charArray.LongLength;
        buffer.Insert(0, charArray[remainder].ToString());
    }
    return buffer.ToString();
}

public static ulong? CompressedStringToULong(string compressedNumber)
{
    if (compressedNumber == null)
        return null;

    if (compressedNumber.Length == 0))
        return 0;

    ulong result   = 0;
    int   baseNum  = _conversionCharacters.Length;
    ulong baseMult = 1;

    for (int i=compressedNumber.Length-1; i>=0; i--)
    {
        int cPos = _conversionCharacters.IndexOf(compressedNumber[i]);
        if (cPos < 0)
            return null;
        result += baseMult * (ulong)cPos;
        baseMult *= (ulong)baseNum;
    }

    return result;
}
Другие вопросы по тегам