如何将38位数字编码为18-20位无特殊字符的字符串?
解决38位数字编码为18-20位A-Z/0-9字符串的方案
首先需要明确你所说的“38位数字”是二进制位还是十进制位,两种情况的解决方案不同:
情况1:38位二进制数字(数值范围0~2³⁸-1)
你当前得到26位Base32字符串的核心原因是编码了整个16字节ByteBuffer(两个long共128位),但实际上只需要编码38位有效数据即可。
具体实现步骤:
- 从ByteBuffer中提取38位有效数值:
通过位运算从两个long中提取目标38位数据:ByteBuffer buffer = ...; // 存储两个long的ByteBuffer long high = buffer.getLong(); long low = buffer.getLong(); // 组合两个long并保留低38位有效数据 BigInteger value = BigInteger.valueOf(high).shiftLeft(64) .or(BigInteger.valueOf(low)) .and(BigInteger.ONE.shiftLeft(38).subtract(BigInteger.ONE)); - 自定义Base36编码(仅用A-Z和0-9):
Base36每个字符可表示约5.17比特,38位二进制仅需8位字符即可容纳,若需要凑到18-20位,可在结果前补前导零:
调用时指定private static final String BASE36_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static String encodeBase36(BigInteger num, int minLength) { StringBuilder sb = new StringBuilder(); if (num.equals(BigInteger.ZERO)) { sb.append('0'); } else { while (num.compareTo(BigInteger.ZERO) > 0) { BigInteger[] divRem = num.divideAndRemainder(BigInteger.valueOf(36)); sb.append(BASE36_CHARS.charAt(divRem[1].intValue())); num = divRem[0]; } } // 补前导零到指定最小长度 while (sb.length() < minLength) { sb.append('0'); } return sb.reverse().toString(); }minLength为18,即可得到符合长度要求的字符串。
情况2:38位十进制数字(数值范围10³⁷~10³⁸-1)
从信息论角度,仅用A-Z和0-9(36种字符)无法将38位十进制数字编码到20位以内:
- 36个字符的20位字符串最多可表示36²⁰≈1.3×10³¹种组合;
- 38位十进制数字有10³⁸种可能,远大于前者的容量,信息量不足以压缩到20位以内。
可选调整方案:
- 扩展字符集:允许使用大小写字母+数字(共62种字符,Base62),此时62²²≈5.4×10³⁸,刚好能容纳38位十进制数字,编码后长度为22位,接近你的需求范围。
- 优化存储方式:直接用
BigInteger存储38位十进制数字,避免ByteBuffer冗余字节带来的编码长度浪费。
内容的提问来源于stack exchange,提问作者Dharun
相关产品推荐
相关产品推荐

