Base62编码解码

编码原理(10进制转62进制):

1.将输入数据视为一个大整数

2.不断除以62,将余数映射为对应的BASE62字符

3.将余数逆序排列得到编码结果

解码原理(62进制转10进制):

1.将每个BASE62字符转换回对应的数值

2.从右到左,每个位置的数值乘以62的相应次方

3.累加所有结果得到原始数据

java 复制代码
package com.ustc.util;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * Base62 编码解码工具类
 * <p>
 * 编码原理(10进制转62进制):
 * 1.将输入数据视为一个大整数
 * 2.不断除以62,将余数映射为对应的BASE62字符
 * 3.将余数逆序排列得到编码结果
 * <p>
 * 解码原理(62进制转10进制):
 * 1.将每个BASE62字符转换回对应的数值
 * 2.从右到左,每个位置的数值乘以62的相应次方
 * 3.累加所有结果得到原始数据
 * @Author: 
 * @Date: 2025-10-25 16:16
 */
public class Base62 {
    // 编码后的结果与这个字符集顺序有关
    private static final String BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    // 基数62
    private static final BigInteger BASE = BigInteger.valueOf(62);

    /**
     * 将字节数组编码为BASE62字符串
     * @param input 输入字节数组
     * @return BASE62编码字符串
     */
    public static String encode(byte[] input) {
        if (input == null || input.length == 0) {
            return "";
        }
        // 将字节数组视为一个大整数
        BigInteger number = new BigInteger(1, input);
        StringBuilder result = new StringBuilder();
        // 实际上就是将十进制数转换为62进制过程
        // 不断除以62,取余数,直到商为0,再将余数映射到62个字符上,最后倒序排列
        while (number.compareTo(BigInteger.ZERO) > 0) {
            // 同时进行除法和取余运算,返回一个包含商和余数的数组
            BigInteger[] divmod = number.divideAndRemainder(BASE);
            number = divmod[0]; // 商
            int remainder = divmod[1].intValue(); // 余数
            result.append(BASE62.charAt(remainder));
        }
        // 处理前导零(BigInteger会忽略前导零,需要特殊处理)
        for (byte b : input) {
            if (b == 0) {
                result.append(BASE62.charAt(0));
            } else {
                break;
            }
        }
        // 反转字符串,因为我们在循环中是先得到低位字符
        return result.reverse().toString();
    }


    /**
     * 将BASE62字符串解码为字节数组
     * @param input BASE62编码字符串
     * @return 原始字节数组
     */
    public static byte[] decode(String input) {
        if (input == null || input.length() == 0) {
            return new byte[0];
        }
        // 实际上就是将62进制数转换为十进制过程
        // 从右往左:余数*(62^0) + 余数*(62^1) + 余数*(62^2) + ...
        BigInteger number = BigInteger.ZERO;
        for (char c : input.toCharArray()) {
            int digit = BASE62.indexOf(c); // 余数
            // number = number * 62 + digit
            number = number.multiply(BASE).add(BigInteger.valueOf(digit));
        }
        // 假如是4位的一个62进制数,上面这个for循环计算过程:
        // number1 = number * 62 + digit1
        // number2 = number1 * 62 + digit2
        // number3 = number2 * 62 + digit3
        // number4 = number3 * 62 + digit4
        // 对循环过程转换一下公式:
        // number1 = number  * 62 + digit1 = number*(62^1) + digit1*(62^0)
        // number2 = number1 * 62 + digit2 = number*(62^2) + digit1*(62^1) + digit2*(62^0)
        // number3 = number2 * 62 + digit3 = number*(62^3) + digit1*(62^2) + digit2*(62^1) + digit3*(62^0)
        // number4 = number3 * 62 + digit4 = number*(62^4) + digit1*(62^3) + digit2*(62^2) + digit3*(62^1) + digit4*(62^0)
        // 因为初始number=0带入上面公式,可以看出刚好就是62进制转10进制的公式,最后number就是转换后的10进制数(大整数)
        // 将大整数转换为字节数组
        byte[] bytes = number.toByteArray();
        // 处理前导零,若最高位为0x00(因BigInteger.toByteArray()会补位),则去除
        if (bytes[0] == 0) {
            // 去掉原数组的第一个字节(索引为 0 的元素),返回一个长度减 1 的新数组
            bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
        }
        // 回顾encode编码的过程,当从首位开始,字节值连续为0,使用第一位字符集填充的这些0
        // 所以这里逆向这个过程,当从首位开始,字符连续为第一位字符集的字符,使用0填充回去。
        int leadingZeroes = 0; //首位开始连续为0的个数(前导零的个数)
        for (char c : input.toCharArray()) {
            if (c == BASE62.charAt(0)) {
                leadingZeroes++;
            } else {
                break;
            }
        }
        // 拼接前导零字节和实际数据
        byte[] result = new byte[leadingZeroes + bytes.length];
        // 前半部分填充0x00
        System.arraycopy(bytes, 0, result, leadingZeroes, bytes.length);
        return result;
    }

    public static void main(String[] args) {
        String input = "你好";
        String encoded = Base62.encode(input.getBytes(StandardCharsets.UTF_8));
        String decoded = new String(Base62.decode(encoded), StandardCharsets.UTF_8);
        System.out.println("原始: " + input);
        System.out.println("加密: " + encoded);
        System.out.println("解密: " + decoded);
    }
}

参考:

https://base62.org/zh-cn/java_sample/