Base32编码和解码问题
题目
你需要实现一个 Base32 的编码和解码函数。
相比于 Base32,你可能更熟悉 Base64,Base64 是非常常见的用字符串形式表示二进制数据的方式,在邮件附件、Web 中的图片中都有广泛的应用。
Base32 是 Base64 的变种,与 Base64 不同的地方在于 Base64 以 6 bit 为一组作为索引,而 Base32 以 5 bit 为一组作为索引,每一组用一个 ASCII 字符表示。Base 64 总共需要 64 个字符表示,而 Base32 则只需要 32 个字符表示。
Base32 的编码流程如下:
- 对二进制数据进行预处理:如果二进制数据的 bit 数目不是 5 的倍数的话,在末尾补 0 直至为 5 的倍数。
- 以 5 bit 为一组进行分组。
- 将每一组的 5 bit 二进制转换为索引(0 - 31)。
- 在索引 - 字符转换表中查询索引对应的字符。
- 根据原始二进制数据的 bit 数目除以 40 后的余数,确定末尾需要补
+的数目。 - 如果原始二进制数据 bit 数目除以 40 后的余数是 0 的话,不需要补
+。 - 如果余数是 8,补 6 个
+。 - 如果余数是 16,补 4 个
+。 - 如果余数是 24,补 3 个
+。 - 如果余数是 32,补 1 个
+。
Base32 的索引 - 字符转换表如下:
索引:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
字符:9 8 7 6 5 4 3 2 1 0 m n b v c x z a s d f g h j k l p o i u y t
你需要对字符串rawStr进行编码,并对encodedStr进行解码。
思路
首先是些许准备工作
首先建立二进制与编码字符的映射,编码用数组表示,解码用哈希表表示
private static final char[] encodingMap = new char[]{'9', '8', '7', '6', '5', '4', '3', '2', '1', '0',
'm', 'n', 'b', 'v', 'c', 'x', 'z', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'p', 'o', 'i', 'u', 'y', 't'};
private static final Map<Character, Integer> decodingMap = new HashMap<>();
static {
for (int i = 0; i < encodingMap.length; i++) {
decodingMap.put(encodingMap[i], i);
}
}
准备工作结束,接下来考虑编码与解码
对于编码,直接模拟即可
对于解码,首先将问题的encodedString用+分割得到多个encoded,根据编码字符串encoded可以得到对应二进制序列encodedBits,这个二进制序列是将原字符串rawStr的二进制表示originalBits末位补0得到的,且补的0的数量不超过4,而原字符串的二进制表示的长度必为8的倍数,所以去掉encodedBits末尾encodedBits % 8位,即得到originalBits
代码
最后附上代码
public class Main {
private static final char[] encodingMap = new char[]{'9', '8', '7', '6', '5', '4', '3', '2', '1', '0',
'm', 'n', 'b', 'v', 'c', 'x', 'z', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'p', 'o', 'i', 'u', 'y', 't'};
private static final Map<Character, Integer> decodingMap = new HashMap<>();
static {
for (int i = 0; i < encodingMap.length; i++) {
decodingMap.put(encodingMap[i], i);
}
}
public static String solution(String rawStr, String encodedString) {
String ret = encode(rawStr);
ret = ret.concat(":");
for (String encoded :
encodedString.split("\+")) {
if (encoded.length() > 0) {
ret = ret.concat(decode(encoded));
}
}
return ret;
}
public static void main(String[] args) {
System.out.println(solution("foo", "b0zj5+++").equals("bljhy+++:bar"));
System.out.println(solution("The encoding process represents 40-bit groups of input bits as output strings of 8 encoded characters. Proceeding from left to right, a 40-bit input group is formed by concatenating 5 8bit input groups. These 40 bits are then treated as 8 concatenated 5-bit groups, each of which is translated into a single character in the base 32 alphabet. When a bit stream is encoded via the base 32 encoding, the bit stream must be presumed to be ordered with the most-significant- bit first. That is, the first bit in the stream will be the high- order bit in the first 8bit byte, the eighth bit will be the low- order bit in the first 8bit byte, and so on.", "bljhy+++b0zj5+++").equals("maf3m164vlahyl60vlds9i6svuahmiod58l3mi6sbglhmodfcbz61b8vb0fj1162c0jjmi6d58jhb160vlk2mu89b0fj1il9b4ls9oogcak2mu89cvp25pncbuls9oo359i79lncbvjh1ln558ahzknsb4aj1lnscbj7917zc0jh3ln4bafhill9bll3yo09vashbu89cajs9id0buf21n89b5z61b8vb0fj1160vlk2mu89bul3yunz58fj3163vul3pln558a2s166vuj33knfbgj37u60vlds9v0928a3su89v4j29unf58dj5oogc8lsi17fv8sj3l093zk79kd0cals9knsbfz21p64vkz21id4b4p3ml89b4ls9c89bvjhiko8cashiknfbgs79v0vb0fj1162c0jjmi6d4zz3mkn6v9z3yla9cuf3sko158fj316fc0zhiiobb4p3ml89v4j21ol9b5z23pncbuh3m166v8zj5kn6casj5160vkz21p6458a37io459ld5168vak3zkn7bgp7i189muf3moa9b5z35pnf58lj1id4b4hs9pnd58shikoxbash116hv4zs9u61bfz35kndbfz63ba9bgj33oo5v4j3cn89caf3m167v4p79iofc0sh7o09vgpj3u89b0ss9i6sbgljmon4bzz21ol9b0ss9oosbasj5ln558ohsu6158p3zl09vgjj3u8vcvfhcod0blfh3kncczhs9kd0czz3bpnscvp7i17fv8zj1160cbh79u61bfz3bpnscvp79kd0czz3soa9caf3m16dcal3mknv58ohso6b58a3m16fv8ss9p60buf7p16xc0s3mia9b0fj1160vkz21p6458d3siddczz6zkd0czz35ynfbfh79u61bfz3mpn2v8p3z167v4p79uo0vah79kd458p3zl09vajjcn09vul31lns58a3su89v4j79u61bfz3bpnscvp79c67v4p79kdlcassk168vls79iox58jhinz+:foobar"));
}
private static String decode(String encodedString) {
int originalBitsLength = encodedString.length() * 5 - (encodedString.length() * 5) % 8;
boolean[] bits = new boolean[originalBitsLength];
for (int i = 0; i < encodedString.length(); i++) {
int index = decodingMap.get(encodedString.charAt(i));
for (int j = 0; j < 5; j++) {
if (i * 5 + 4 - j < bits.length) {
bits[i * 5 + 4 - j] = (index & 1) == 1;
}
index >>= 1;
}
}
return new String(getBytes(bits));
}
private static String encode(String rawStr) {
boolean[] bits = getBits(rawStr.getBytes(StandardCharsets.US_ASCII));
String encoded = "";
int mod = bits.length % 40;
for (int i = 0; i < bits.length; i += 5) {
int index = 0, increase = 1;
for (int j = 4; j >= 0; j--) {
if (i + j < bits.length && bits[i + j]) {
index += increase;
}
increase *= 2;
}
encoded = encoded.concat(String.valueOf(encodingMap[index]));
}
if (mod == 8) {
encoded = encoded.concat("++++++");
} else if (mod == 16) {
encoded = encoded.concat("++++");
} else if (mod == 24) {
encoded = encoded.concat("+++");
} else if (mod == 32) {
encoded = encoded.concat("+");
}
return encoded;
}
private static boolean[] getBits(byte[] bytes) {
boolean[] booleans = new boolean[bytes.length * 8];
byte one = 1;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
for (int j = 0; j < 8; j++) {
booleans[i * 8 + 7 - j] = (b & one) == 1;
b >>= 1;
}
}
return booleans;
}
private static byte[] getBytes(boolean[] bits) {
byte[] bytes = new byte[bits.length / 8];
for (int i = 0; i < bits.length; i += 8) {
byte sum = 0, increase = 1;
for (int j = 0; j < 8; j++) {
if (bits[i + 7 - j]) {
sum |= increase;
}
increase <<= 1;
}
bytes[i / 8] = sum;
}
return bytes;
}
}