Skip to content

Fortytwoo/json_compression

Repository files navigation

JSON 压缩工具

一个功能强大的 Python JSON 数据压缩工具,支持多种压缩算法和策略,可根据不同场景自动选择最优压缩方案。

特性

  • 🚀 多种压缩算法:Gzip、Zlib、Bz2、LZMA
  • 🔑 智能键名压缩:自动缩短 JSON 键名
  • 🎯 自动选择:根据数据大小和优先级自动选择最佳压缩方案
  • 📊 压缩统计:详细的压缩率和性能对比
  • 💾 Base64 编码:压缩结果可直接传输或存储

安装

# 克隆仓库
git clone <repository-url>
cd json_compression

# 使用 uv 创建虚拟环境(如需要)
uv venv

本工具仅使用 Python 标准库,无需额外安装依赖。

快速开始

运行演示

# 运行完整演示(包含所有压缩方法对比)
uv run .\json_compressor.py

# 仅运行自动压缩演示
uv run .\json_compressor.py --auto

# 显示帮助信息
uv run .\json_compressor.py --help

作为模块使用

from json_compressor import JSONCompressor

# 示例数据
data = {
    "user_id": 12345,
    "username": "张三",
    "items": [1, 2, 3, 4, 5]
}

# 快速压缩(推荐 Zlib,速度快,压缩率好)
compressed = JSONCompressor.compress_with_zlib(data)
print(f"压缩后: {len(compressed)} 字节")

# 解压
original = JSONCompressor.decompress_zlib(compressed)
print(f"解压验证: {original == data}")

# 自动选择最佳压缩方法
result = JSONCompressor.compress_auto(data, priority='ratio')
original = JSONCompressor.decompress_advanced(result)

压缩率计算公式

基本压缩率公式

压缩率 = (1 - 压缩后大小 / 原始大小) × 100%

示例:

  • 原始大小:482 字节
  • 压缩后大小:428 字节
  • 压缩率 = (1 - 428/482) × 100% = 11.20%

节省字节数

节省字节数 = 原始大小 - 压缩后大小

说明:

  • 压缩率为正数表示数据被成功压缩,数值越大压缩效果越好
  • 压缩率为负数表示压缩后体积反而增大(常见于极小数据或已压缩数据)

相对原始大小的压缩率

在对比表格中,相对格式化 JSON 的压缩率计算:

相对压缩率 = (1 - 当前大小 / 格式化JSON大小) × 100%

示例(基于运行结果):

  • 格式化 JSON:646 字节
  • Zlib 压缩:428 字节
  • 相对压缩率 = (1 - 428/646) × 100% = 33.7%

压缩方法原理与对比

1. JSON 最小化(Minify)

原理:

  • 去除 JSON 中的所有空格、换行符、缩进
  • 不进行任何实际压缩,仅删除无用字符
  • 完全可逆,不损失任何数据

特点:

  • ✅ 速度极快(几乎无开销)
  • ✅ 保持 JSON 可读性(可直接解析)
  • ✅ 无需解压步骤
  • ⚠️ 压缩率有限(通常 20-30%)

适用场景:

  • API 响应优化
  • 前端传输
  • 需要保持 JSON 格式的场景

典型压缩率: 20-30%


2. Gzip 压缩

原理:

  • 基于 DEFLATE 算法(LZ77 + Huffman 编码)
  • LZ77:查找重复字符串,用指针替换(距离+长度)
  • Huffman 编码:高频字符用短编码,低频字符用长编码
  • 包含 CRC32 校验和文件头

技术细节:

  1. 滑动窗口搜索重复模式(默认 32KB 窗口)
  2. 用 (距离, 长度) 对替换重复数据
  3. 对结果进行 Huffman 编码

特点:

  • ✅ HTTP 标准压缩格式(浏览器原生支持)
  • ✅ 速度与压缩率平衡良好
  • ✅ 广泛应用于 Web 传输
  • ⚠️ 额外的文件头开销(~18 字节)

适用场景:

  • HTTP 响应压缩(Content-Encoding: gzip)
  • Web API 数据传输
  • 实时数据流

典型压缩率: 30-40%(小数据),70-90%(大数据)


3. Zlib 压缩

原理:

  • 同样基于 DEFLATE 算法
  • 比 Gzip 更简洁的封装格式(2 字节头 + 4 字节校验)
  • 使用 Adler-32 校验(比 CRC32 更快)

与 Gzip 的区别:

  • 文件头更小(2 字节 vs 10 字节)
  • 校验和算法不同(Adler-32 vs CRC32)
  • 压缩核心算法完全相同

特点:

  • ✅ 速度最快(三者中)
  • ✅ 开销更小
  • ✅ 内存占用少
  • ✅ Python 标准库支持

适用场景:

  • 通用数据压缩
  • 实时传输(游戏、聊天)
  • 内存受限环境
  • 推荐作为默认选择

典型压缩率: 30-40%(小数据),70-90%(大数据)

性能对比(Zlib vs Gzip):

  • 压缩速度:Zlib 快 5-10%
  • 压缩率:基本相同(差异 < 1%)
  • 解压速度:Zlib 快 5-15%

4. Bz2 压缩(高压缩率)

原理:

  • 基于 Burrows-Wheeler 变换 (BWT) + RLE + Huffman 编码
  • BWT:将相似字符聚集在一起(可逆排序变换)
  • RLE:压缩连续重复字符
  • Huffman:熵编码

技术流程:

  1. Burrows-Wheeler 变换(重排数据使相似字符相邻)
  2. Move-to-Front 变换(减小字符编码值)
  3. Run-Length 编码(压缩连续字符)
  4. Huffman 编码(熵编码)

特点:

  • ✅ 高压缩率(比 Gzip/Zlib 高 15-30%)
  • ✅ 对大数据效果显著
  • ⚠️ 速度较慢(比 Zlib 慢 3-5 倍)
  • ⚠️ 内存占用较高(块大小通常 100-900KB)
  • ⚠️ 小数据可能负优化(< 1KB 时压缩率可能为负)

适用场景:

  • 大文件压缩
  • 数据归档
  • 对速度要求不高但需要高压缩率的场景
  • 离线批处理

典型压缩率: 10-30%(小数据),85-95%(大数据)


5. LZMA 压缩(最高压缩率)

原理:

  • 基于 Lempel-Ziv-Markov chain Algorithm
  • 使用超大字典(可达数 MB)
  • 结合 LZ77 + 范围编码(Range Encoding)
  • 高度优化的匹配查找算法

技术特点:

  1. 巨大的滑动窗口(默认 8MB,可达 1.5GB)
  2. 复杂的匹配查找(多种匹配模式)
  3. 上下文相关的范围编码
  4. 多线程支持(在大数据时)

特点:

  • ✅ 最高压缩率(通常比 Bz2 再高 10-20%)
  • ✅ 对大数据集效果极佳
  • ⚠️ 速度最慢(比 Zlib 慢 5-10 倍)
  • ⚠️ 内存占用大(压缩时可能需要数十 MB)
  • ⚠️ 小数据开销大(文件头等元数据占比高)

适用场景:

  • 长期存储归档
  • 文件分发(软件安装包)
  • 数据备份
  • 对压缩率要求极高的场景

典型压缩率: -10% 到 20%(小数据),90-99%(大数据)


6. 键名压缩

原理:

  • 将 JSON 中的长键名映射为短键名
  • 自动生成映射表:a, b, c, ..., z, A, B, ..., aa, ab, ...
  • 保存映射表用于解压恢复

示例:

原始数据:

{
  "user_information": {"first_name": "张三"},
  "order_details": [{"order_id": "001"}]
}

压缩后:

{
  "a": {"b": "张三"},
  "c": [{"d": "001"}]
}

映射表:

{
  "a": "user_information",
  "b": "first_name",
  "c": "order_details",
  "d": "order_id"
}

特点:

  • ✅ 对重复结构数据效果显著
  • ✅ 可与其他压缩算法组合
  • ✅ 减少数据冗余
  • ⚠️ 需要传输映射表(增加开销)
  • ⚠️ 小数据或键名本身很短时效果不明显

适用场景:

  • 大量相同结构的 JSON 对象(如数组记录)
  • 键名很长的数据
  • 与 LZMA/Bz2 组合使用

典型压缩率: 40-60%(独立使用),配合 LZMA 可达 99%


7. LZMA + 键名组合压缩

原理:

  1. 先进行键名压缩,减少重复键名
  2. 再用 LZMA 压缩已优化的数据
  3. 两阶段压缩互补增强

特点:

  • ✅ 最高压缩率(大数据集可达 99%)
  • ✅ 适合重复结构的大数据
  • ⚠️ 速度最慢
  • ⚠️ 解压需要两步(先解压 LZMA,再还原键名)
  • ⚠️ 小数据开销大(映射表 + LZMA 头 > 节省量)

适用场景:

  • 大量重复结构数据的长期存储
  • 数据归档
  • 需要极致压缩率的场景

典型压缩率: 90-99%(大数据),可能为负(小数据)


压缩方法性能对比

速度对比(从快到慢)

Minify > Zlib ≈ Gzip > Bz2 > LZMA

相对速度:

  • Minify:基准(几乎无开销)
  • Zlib:1.0x(推荐基准)
  • Gzip:0.9x(比 Zlib 慢 ~10%)
  • Bz2:0.3x(比 Zlib 慢 3-5 倍)
  • LZMA:0.1-0.2x(比 Zlib 慢 5-10 倍)

压缩率对比(从高到低)

小数据(< 1KB):

Minify (25%) > Zlib/Gzip (30-40%) > 键名压缩 (40-50%) > Bz2/LZMA(可能负优化)

中等数据(1KB - 100KB):

LZMA (70-85%) > Bz2 (65-80%) > Zlib/Gzip (60-75%) > 键名压缩 (50-60%)

大数据(> 100KB):

LZMA+键名 (95-99%) > LZMA (90-98%) > Bz2 (85-95%) > Zlib/Gzip (75-90%)

内存占用(压缩时)

方法 内存占用 说明
Minify 极小 仅需原数据 1 份拷贝
Zlib/Gzip ~256KB + 数据大小
Bz2 中等 ~8MB(块大小 × 5)
LZMA ~64MB+(字典大小相关)
键名压缩 需存储映射表

选择建议

根据场景选择

场景 推荐方法 原因
HTTP API 传输 Zlib 或 Gzip 速度快,浏览器原生支持
实时通信 Zlib 最快,开销最小
数据存储 LZMA 或 Bz2 最高压缩率,节省存储空间
大数据归档 LZMA + 键名 极致压缩率
前端可见 Minify 保持可读性,无需解压
微服务间通信 Zlib 低延迟,CPU 开销小
日志归档 Bz2 或 LZMA 高压缩率,读取频率低
移动端传输 Zlib 省电,速度快

根据数据大小选择

数据大小 推荐方法 备选
< 100 字节 Minify 不压缩(开销大于收益)
100B - 1KB Zlib Minify
1KB - 10KB Zlib Gzip, Bz2
10KB - 100KB Zlib 或 Bz2 LZMA(压缩率优先)
> 100KB LZMA + 键名 Bz2

根据优先级选择

速度优先(Speed Priority):

# 实时系统、高并发场景
result = JSONCompressor.compress_auto(data, priority='speed')
# 自动选择:Zlib(所有数据大小)

平衡模式(Balanced):

# 通用场景
result = JSONCompressor.compress_auto(data, priority='balanced')
# 自动选择:
# - < 100KB: Zlib
# - > 100KB: Bz2

压缩率优先(Ratio Priority):

# 存储、归档场景
result = JSONCompressor.compress_auto(data, priority='ratio')
# 自动选择:
# - < 10KB: Zlib
# - 10-50KB: Bz2
# - > 50KB: LZMA + 键名压缩

API 文档

基础压缩方法

compress_with_zlib(data, level=9)

使用 Zlib 压缩数据(推荐)。

参数:

  • data: 字典、列表或 JSON 字符串
  • level: 压缩级别 (0-9),9 为最高

返回: Base64 编码的压缩数据

示例:

compressed = JSONCompressor.compress_with_zlib(data, level=6)

decompress_zlib(compressed_data)

解压 Zlib 数据。

参数:

  • compressed_data: Base64 编码的压缩数据

返回: 原始 Python 对象


compress_with_gzip(data, level=9)

使用 Gzip 压缩数据(HTTP 标准)。

compress_with_bz2(data, level=9)

使用 Bz2 压缩数据(高压缩率)。

compress_with_lzma(data, preset=9)

使用 LZMA 压缩数据(最高压缩率)。


键名压缩

compress_keys(data, key_map=None)

压缩 JSON 键名。

返回: (压缩后的数据, 键名映射表)

示例:

compressed_data, reverse_map = JSONCompressor.compress_keys(data)
# 保存 reverse_map 用于解压

decompress_keys(data, reverse_map)

还原键名。


高级方法

compress_advanced(data, compression_type='gzip', compress_keys=False)

组合压缩策略。

参数:

  • compression_type: 'gzip', 'zlib', 'bz2', 'lzma'
  • compress_keys: 是否同时压缩键名

返回: 包含压缩数据和元信息的字典

示例:

result = JSONCompressor.compress_advanced(
    data, 
    compression_type='lzma',
    compress_keys=True
)
# 传输 result 字典

decompress_advanced(compressed_result)

解压高级压缩结果。


compress_auto(data, priority='ratio', compress_keys=True)

自动选择最佳压缩方法。

参数:

  • priority: 'speed', 'balanced', 'ratio'
  • compress_keys: 是否尝试键名压缩

返回: 自动选择的压缩结果

示例:

# 自动选择,压缩率优先
result = JSONCompressor.compress_auto(data, priority='ratio')
original = JSONCompressor.decompress_advanced(result)

工具方法

minify(data)

最小化 JSON(去除格式化)。

返回: 最小化的 JSON 字符串

get_compression_stats(original_data, compressed_data)

获取压缩统计信息。

返回:

{
    'original_size': 1000,
    'compressed_size': 300,
    'saved_bytes': 700,
    'compression_ratio': '70.00%'
}

运行结果示例

基于提供的运行输出,以下是各方法的实际压缩效果:

小数据测试(646 字节格式化,482 字节最小化)

方法 压缩后大小 压缩率 相对格式化 JSON
Gzip 444 字节 7.88% -31.3%
Zlib 428 字节 11.20% -33.7%
Bz2 492 字节 -2.07% -23.8%
LZMA 504 字节 -4.56% -22.0%
键名压缩 285 字节 40.87% -55.9%
LZMA+键名 728 字节 -51.04% +12.7%

分析:

  • 小数据时,Zlib 表现最佳
  • Bz2 和 LZMA 因文件头开销导致负优化
  • 键名压缩单独使用效果最好
  • LZMA+键名组合反而增大(开销过高)

大数据测试(1000 条记录,417,411 字节 = 407.63 KB)

方法 压缩后大小 压缩率 实际大小
Minify 271,653 字节 34.92% 265.29 KB
Gzip 13,428 字节 96.78% 13.11 KB
Zlib 13,412 字节 96.79% 13.10 KB
Bz2 6,560 字节 98.43% 6.41 KB
LZMA 4,264 字节 98.98% 4.16 KB
键名压缩 195,647 字节 53.13% 191.06 KB
LZMA+键名 3,856 字节 99.08% 3.77 KB

分析:

  • 大数据时,LZMA 优势明显(最高压缩率)
  • Bz2 性能也很好(98.43%)
  • LZMA+键名组合达到 99.08% 的极致压缩率
  • Gzip/Zlib 在大数据时压缩率也很高(96%+)

实际应用示例

示例 1:API 响应压缩

from json_compressor import JSONCompressor
from flask import Flask, jsonify, Response

app = Flask(__name__)

@app.route('/api/data')
def get_data():
    data = get_large_dataset()  # 获取大数据
    
    # 使用 Zlib 压缩(速度快)
    compressed = JSONCompressor.compress_with_zlib(data)
    
    return Response(
        compressed,
        mimetype='application/json',
        headers={'Content-Encoding': 'custom-zlib'}
    )

示例 2:数据存储优化

import json
from json_compressor import JSONCompressor

# 存储数据
def save_to_file(data, filename):
    result = JSONCompressor.compress_advanced(
        data,
        compression_type='lzma',
        compress_keys=True
    )
    
    with open(filename, 'w') as f:
        json.dump(result, f)

# 读取数据
def load_from_file(filename):
    with open(filename, 'r') as f:
        compressed_result = json.load(f)
    
    return JSONCompressor.decompress_advanced(compressed_result)

示例 3:批量数据处理

def process_batch(records):
    """处理大批量记录"""
    # 自动选择最佳压缩
    result = JSONCompressor.compress_auto(
        records,
        priority='ratio'  # 压缩率优先
    )
    
    print(f"原始大小: {result['data_size']} 字节")
    print(f"压缩方法: {result['type']}")
    print(f"键名压缩: {'是' if result['key_compressed'] else '否'}")
    
    return result

性能测试

测试环境

  • Windows 11
  • Python 3.x
  • PowerShell

测试结果

小数据(< 1KB):

  • 推荐:Zlib 或键名压缩
  • 避免:LZMA、Bz2(负优化)

中等数据(1KB - 100KB):

  • 平衡:Zlib(速度优先)
  • 压缩率:Bz2 或 LZMA

大数据(> 100KB):

  • 最佳:LZMA + 键名压缩(99%+ 压缩率)
  • 速度与压缩率平衡:Bz2

注意事项

  1. 小数据慎用高级压缩:Bz2 和 LZMA 在小数据(< 1KB)时可能导致体积增大
  2. 传输键名映射表:使用键名压缩时,必须保存并传输映射表
  3. Base64 开销:所有压缩结果都经过 Base64 编码,增加约 33% 体积(但便于传输)
  4. 内存限制:LZMA 压缩大数据时可能占用大量内存
  5. CPU 使用:LZMA 和 Bz2 会占用更多 CPU 时间

常见问题

Q1:为什么小数据压缩率是负数?

A: 压缩算法需要存储元数据(文件头、字典、校验和等)。当数据很小时,这些开销可能超过压缩节省的空间。

解决方案:

  • 小于 100 字节:建议仅使用 Minify
  • 100-1000 字节:使用 Zlib
  • 大于 1KB:可以考虑更高级的压缩

Q2:Zlib 和 Gzip 有什么区别?

A: 两者使用相同的 DEFLATE 压缩算法,差异仅在于封装格式:

  • Zlib:2 字节头 + 压缩数据 + 4 字节 Adler-32 校验
  • Gzip:10 字节头 + 压缩数据 + 8 字节尾(CRC32 + 原始大小)

选择建议:

  • HTTP 标准:Gzip
  • 通用场景:Zlib(更快,开销更小)

Q3:什么时候使用键名压缩?

A: 键名压缩适用于:

  • 大量相同结构的对象(如数组记录)
  • 键名很长的数据
  • 配合 LZMA 用于极致压缩

不适用:

  • 键名本身很短(如 id, name
  • 小数据(映射表开销大)

Q4:如何选择压缩级别?

A: 压缩级别(0-9)的权衡:

  • 0:不压缩(仅封装)
  • 1-3:低压缩,快速
  • 6:默认平衡点(推荐)
  • 9:最高压缩,较慢

建议:

  • 实时传输:Level 3-6
  • 存储归档:Level 9

贡献

欢迎提交 Issue 和 Pull Request!


许可证

MIT License


更多资源


最后更新: 2024-11-06

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages