短码生成与还原方案:JS 实现数字与短字符串可逆转换 36 进制编码

2026-02-06 67 浏览 0 评论

在短链接生成、订单号脱敏、资源标识压缩等业务场景中,常需将冗长数字转为简短字符串(短码),且必须保证转换可逆,以便从短码回溯原始数字。本文将分享一套基于 36 进制编码的 JavaScript 实现方案,封装转换与还原两大核心方法,兼顾实用性、健壮性与边界处理,可直接落地到短链接生成等实际业务中。

核心思路:基于 36 进制的编码逻辑

数字与短字符串的可逆转换,本质是进制之间的相互转换。选用 36 进制作为中间载体,原因在于其字符集由 0-9、a-z 组成,共 36 个字符,无需特殊符号,兼容性强(可直接用于 URL、数据库存储等场景),同时能有效缩短数字长度 - 相比 10 进制,36 进制表示同一数字的字符串长度可大幅减少(例如 123456789 的 10 进制表示为 9 位,36 进制仅为 6 位 21i3v9 )。 编码过程(数字→短字符串):将 10 进制数字通过 取余→记录字符→整数除法 循环处理,最终反转结果得到 36 进制字符串;解码过程(短字符串→数字):遍历字符串,按 36 进制权重累加计算,还原为 10 进制数字,确保转换可逆。

完整实现代码

以下是封装好的两个核心方法,包含严格的输入校验、边界处理,可直接集成到项目中使用。


/**
 * 将 10 进制数字转换为 0-9、a-z 组成的短字符串(36 进制编码)
 * @param {number} num - 非负整数(原始数字)
 * @returns {string} 36 进制短字符串
 * @throws {Error} 输入非非负整数时抛出异常
 */
function numToShortStr(num) {
    // 校验输入类型和范围
    if (typeof num !== 'number' || !Number.isInteger(num) || num < 0) {
        throw new Error('仅支持非负整数转换');
    }

    // 定义 36 进制字符集(0-9 + a-z)
    const chars = '0123456789abcdefghijklmnopqrstuvwxyz';
    const base = chars.length;

    // 特殊处理数字 0
    if (num === 0) {
        return '0';
    }

    let result = [];
    while (num > 0) {
        // 取余数,对应字符集的索引
        const remainder = num % base;
        result.push(chars[remainder]);
        // 更新数字为商(整数除法)
        num = Math.floor(num / base);
    }

    // 余数是逆序的,反转后拼接成字符串
    return result.reverse().join('');
}

/**
 * 将 36 进制短字符串还原为 10 进制数字(解码)
 * @param {string} str - 0-9、a-z 组成的字符串
 * @returns {number} 原始 10 进制数字
 * @throws {Error} 输入非法字符/非字符串时抛出异常
 */
function shortStrToNum(str) {
    // 校验输入类型
    if (typeof str !== 'string') {
        throw new Error('输入必须是字符串类型');
    }

    const chars = '0123456789abcdefghijklmnopqrstuvwxyz';
    const base = chars.length;
    // 统一转为小写,兼容大写输入
    const lowerStr = str.toLowerCase();

    // 校验字符串是否包含非法字符
    for (const char of lowerStr) {
        if (!chars.includes(char)) {
            throw new Error(`包含非法字符:${char},仅支持 0-9、a-z`);
        }
    }

    let num = 0;
    for (const char of lowerStr) {
        // 字符转对应数值
        const val = chars.indexOf(char);
        num = num * base + val;
    }
    return num;
}

代码细节解析

1. 输入校验机制

健壮的输入校验是避免转换异常的关键,两个方法分别针对输入做了严格限制: 编码方法 numToShortStr 仅接受非负整数,过滤小数、负数、非数字类型,防止因输入不规范导致的逻辑错误;解码方法 shortStrToNum 首先校验输入为字符串类型,再通过遍历判断是否包含 0-9、a-z 以外的非法字符,同时自动将输入转为小写,兼容大写字符串输入场景,提升方法的灵活性。

2. 边界场景处理

针对数字 0 做了特殊处理 - 若不单独处理,0 会因循环条件不满足导致返回空字符串,不符合实际使用预期。通过直接返回 0 ,确保边界值转换的正确性。

3. 可逆性保障

编码时通过取余记录字符,最终反转结果是因为取余得到的字符顺序与 36 进制字符串顺序相反;解码时按字符串顺序遍历,每一位字符对应的数值乘以 36 的对应权重(从左到右权重依次递增),累加后即可准确还原原始数字,全程无信息丢失,保证转换可逆。

实战测试示例

为验证方法的正确性,可通过以下测试代码验证编码、解码全流程,覆盖普通数字、边界值 0 等场景:


(function test() {
    const originalNum = 123456789; // 原始数字
    try {
        // 编码
        const shortStr = numToShortStr(originalNum);
        console.log('原始数字:', originalNum); // 输出:123456789
        console.log('转换后的短字符串:', shortStr); // 输出:21i3v9

        // 解码还原
        const restoredNum = shortStrToNum(shortStr);
        console.log('还原后的数字:', restoredNum); // 输出:123456789
        console.log('是否可逆:', originalNum === restoredNum); // 输出:true

        // 测试边界值 0
        console.log('0 转换结果:', numToShortStr(0)); // 输出:0
        console.log('0 还原结果:', shortStrToNum('0')); // 输出:0
    } catch (e) {
        console.error('转换失败:', e.message);
    }
})();

运行测试代码后,控制台将输出编码结果、还原结果及可逆性验证,可直观确认方法的正确性。

扩展方案:62 进制进一步缩短长度

若需要更短的字符串长度,可将编码方案改为 62 进制(字符集包含 0-9、a-z、A-Z),仅需修改字符集和进制基数即可,核心逻辑保持不变。调整后的字符集与基数如下:


// 62 进制字符集
const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const base = 62; // 基数改为 62

62 进制相比 36 进制,字符数量更多,相同数字转换后的字符串长度更短,适合对字符串长度有极致要求的场景(如短链接生成)。需注意的是,62 进制区分大小写,若需兼容不区分大小写的场景,建议仍使用 36 进制方案。

注意事项

JavaScript 中的 Number 类型能安全表示的整数范围为 2^53 - 1 (即 9007199254740991),超出该范围的整数转换可能会丢失精度。若需处理超大整数,可将方法中的 Number 类型改为 BigInt,适配更大范围的数字转换。

总结

本文分享的 36 进制编码方案,通过简洁的 JavaScript 代码实现了数字与短字符串的可逆转换,兼顾了兼容性、健壮性和实用性。核心逻辑清晰易懂,可直接集成到各类项目中,满足 URL 参数简化、标识压缩等开发需求。同时,通过调整字符集可扩展为 62 进制方案,灵活适配不同的长度要求,是开发中处理数字与短字符串转换的优质方案。


发布评论

发布评论前请先 登录
取消
0 评论
点赞
收藏

评论列表 0

暂无评论