我有一个十六进制的128位数字存储在一个字符串中(来自md5,这里不担心安全性),我想将其转换为base-36字符串。如果它是一个64位或更少的数字我将它转换为64位整数然后使用我发现的算法将整数转换为基数为36的字符串但这个数字太大了所以我有点在如何处理这个问题的损失。任何指导都将不胜感激。
编辑:在Roland Illig指出通过电话说0 / O和1 / l并且没有获得超过十六进制的数据密度的麻烦之后我想我可能最终会留下十六进制。如果有一种相对简单的方法可以将任意长度的十六进制字符串转换为base-36字符串,我仍然很好奇。
答案 0 :(得分:6)
base-36编码需要6位来存储每个令牌。与base-64相同但不使用28个可用令牌。求解36 ^ n> = 2 ^ 128得到n> = log(2 ^ 128)/ log(36)或25个令牌以对该值进行编码。
base-64编码也需要6位,所有可能的令牌值都使用。求解64 ^ n> = 2 ^ 128得到n> = log(2 ^ 128)/ log(64)或22个令牌以对该值进行编码。
计算base-36编码需要除以36的幂。没有简单的快捷方式,你需要一个可以使用128位值的除法算法。 base-64编码更容易计算,因为它是2的幂。只需一次取6位并移位6,总共22次消耗所有128位。
为什么要使用base-36? Base-64编码器是标准配置。如果你真的对令牌空间有一个约束(你不应该,ASCII rulez),那么至少使用base-32编码。或者任何2的幂,base-16是十六进制。
答案 1 :(得分:1)
如果唯一缺少的是支持128位无符号整数,这里有适合您的解决方案:
#include <stdio.h>
#include <inttypes.h>
typedef struct {
uint32_t v3, v2, v1, v0;
} uint128;
static void
uint128_divmod(uint128 *out_div, uint32_t *out_mod, const uint128 *in_num, uint32_t in_den)
{
uint64_t x = 0;
x = (x << 32) + in_num->v3;
out_div->v3 = x / in_den;
x %= in_den;
x = (x << 32) + in_num->v2;
out_div->v2 = x / in_den;
x %= in_den;
x = (x << 32) + in_num->v1;
out_div->v1 = x / in_den;
x %= in_den;
x = (x << 32) + in_num->v0;
out_div->v0 = x / in_den;
x %= in_den;
*out_mod = x;
}
int
main(void)
{
uint128 x = { 0x12345678, 0x12345678, 0x12345678, 0x12345678 };
uint128 result;
uint32_t mod;
uint128_divmod(&result, &mod, &x, 16);
fprintf(stdout, "%08"PRIx32" %08"PRIx32" %08"PRIx32" %08"PRIx32" rest %08"PRIx32"\n", result.v3, result.v2, result.v1, result.v0, mod);
return 0;
}
使用此功能,您可以重复计算mod-36结果,从而获得编码为base-36的数字。
答案 2 :(得分:1)
如果您在.NET 4中使用C ++,则可以始终使用System.Numerics.BigInteger类。您可以尝试调用其中一个toString覆盖,以使您获得36。
或者查看许多Big Integer库中的一个,例如Matt McCutchen's C++ Big Integer Library虽然您可能需要查看depths of the classes以使用自定义库,例如36。
答案 3 :(得分:1)
两件事:
1.将字节字符串除以36并不困难。但如果你不能打算实现它,你可以使用base-32编码,这需要26个字节而不是25个。
2.如果你想通过手机向人类阅读结果,你绝对必须在你的字符串中添加一个简单的校验和,这将花费一到两个字节,但会为你节省大量的“中国私语”麻烦。听力障碍的客户。