You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart如何实现字符串双向缩短与原串可逆还原功能

Dart实现长字符串与短字符串的无损双向转换
  • 开发语言:Dart
  • 核心需求:针对形如3ZwgC3xOuq1LpbL4Ajs5这类字符集为数字+大小写字母的长ID,实现双向转换能力
    • 正向能力:将长字符串压缩为类似3DV3A2格式的短字符串
    • 反向能力:传入压缩后的短字符串,可无损还原得到原始长字符串
  • 转换参考示例:

    3ZwgC3xOuq1LpbL4Ajs5 → 压缩 → 3DV3A2
    3DV3A2 → 还原 → 3ZwgC3xOuq1LpbL4Ajs5


实现原理

你使用的长字符串属于Base62字符集(包含0-9、a-z、A-Z共62个可打印字符):

  • 如果允许短串包含特殊可打印字符,可通过高进制转换实现纯算法无损压缩,不需要存储任何映射关系,压缩率约10%~15%
  • 如果要求短串也仅使用数字+大小写字母(和示例格式完全一致),受信息熵限制,同字符集下无法通过纯算法实现压缩,需要通过双向映射表实现,适合ID总量可控的业务场景

注意:不存在纯算法实现的、同Base62字符集下的任意长度字符串无损压缩,所有声称无需存储就能把长Base62串压成更短Base62串且可还原的方案都不符合信息论基本原理


完整代码实现

方案1:纯算法高进制压缩(无存储依赖)

class Base62HighBaseCompressor {
  // 原始Base62字符集,顺序一旦确定不能修改,否则转换会出错
  static const String _base62Chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  // 高基数字符集,使用所有ASCII可见字符(!到~共94个)提升编码密度
  static const String _highBaseChars = '!\"#\$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';

  static final Map<String, int> _base62Index = {
    for (int i = 0; i < _base62Chars.length; i++) _base62Chars[i]: i
  };
  static final Map<String, int> _highBaseIndex = {
    for (int i = 0; i < _highBaseChars.length; i++) _highBaseChars[i]: i
  };

  /// 长Base62字符串压缩为短字符串
  static String compress(String longStr) {
    BigInt num = BigInt.zero;
    for (final c in longStr.split('')) {
      final idx = _base62Index[c];
      if (idx == null) throw ArgumentError('输入包含非法Base62字符: $c');
      num = num * BigInt.from(62) + BigInt.from(idx);
    }
    if (num == BigInt.zero) return _highBaseChars[0];
    final result = StringBuffer();
    final highBase = BigInt.from(_highBaseChars.length);
    while (num > BigInt.zero) {
      final remainder = num % highBase;
      result.write(_highBaseChars[remainder.toInt()]);
      num = num ~/ highBase;
    }
    return result.toString().split('').reversed.join();
  }

  /// 短字符串解压还原为原始Base62长字符串
  static String decompress(String shortStr) {
    BigInt num = BigInt.zero;
    for (final c in shortStr.split('')) {
      final idx = _highBaseIndex[c];
      if (idx == null) throw ArgumentError('输入包含非法压缩字符: $c');
      num = num * BigInt.from(_highBaseChars.length) + BigInt.from(idx);
    }
    if (num == BigInt.zero) return _base62Chars[0];
    final result = StringBuffer();
    final base62 = BigInt.from(62);
    while (num > BigInt.zero) {
      final remainder = num % base62;
      result.write(_base62Chars[remainder.toInt()]);
      num = num ~/ base62;
    }
    return result.toString().split('').reversed.join();
  }
}

方案2:双向映射实现(短串为纯字母数字,和示例格式一致)

class Base62IdMapper {
  // 双向映射表,生产环境需要持久化存储(如存数据库),服务重启时加载
  static final Map<String, String> _longToShort = {};
  static final Map<String, String> _shortToLong = {};
  // 短串字符集,顺序固定不要修改
  static const String _charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  // 自增计数器,生产环境需要持久化,重启后从已生成的最大短ID对应数值继续计数
  static BigInt _counter = BigInt.zero;

  /// 传入长ID,返回对应纯字母数字格式的短ID
  static String toShort(String longId) {
    if (_longToShort.containsKey(longId)) return _longToShort[longId]!;
    BigInt num = _counter;
    final shortBuf = StringBuffer();
    if (num == BigInt.zero) shortBuf.write(_charset[0]);
    while (num > BigInt.zero) {
      final remainder = num % BigInt.from(62);
      shortBuf.write(_charset[remainder.toInt()]);
      num = num ~/ BigInt.from(62);
    }
    final shortId = shortBuf.toString().split('').reversed.join();
    _longToShort[longId] = shortId;
    _shortToLong[shortId] = longId;
    _counter += BigInt.one;
    return shortId;
  }

  /// 传入短ID,还原对应原始长ID
  static String toLong(String shortId) {
    if (!_shortToLong.containsKey(shortId)) {
      throw ArgumentError('短ID不存在对应映射: $shortId');
    }
    return _shortToLong[shortId]!;
  }
}

调用示例

void main() {
  // 高进制压缩方案调用
  const originalLong = '3ZwgC3xOuq1LpbL4Ajs5';
  final compressed = Base62HighBaseCompressor.compress(originalLong);
  print('压缩后短串: $compressed');
  final recovered = Base62HighBaseCompressor.decompress(compressed);
  print('还原后长串: $recovered');
  print('是否无损还原: ${recovered == originalLong}'); // 输出true

  // 映射方案调用(短串为纯字母数字)
  final shortId = Base62IdMapper.toShort('3ZwgC3xOuq1LpbL4Ajs5');
  print('映射生成短ID: $shortId'); // 输出类似3DV3A2格式的短串
  final longId = Base62IdMapper.toLong(shortId);
  print('映射还原长ID: $longId'); // 输出原始长串
}

内容的提问来源于stack exchange,提问作者Emirhan Gümüş

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:01:19