Кратчайший способ представления 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;
}
Вот реализация: базовое преобразование очень длинных положительных целых чисел