Swift中NSMutableData压缩的LZ4数据,Python解压失败求助
Swift LZ4压缩数据无法用Python lz4库解压问题排查
问题场景
我在Swift中通过以下代码使用NSMutableData压缩随机数据,并将压缩结果保存为compressed.dat文件:
import Foundation let n = 1024 let numbers = (0..<n).map { _ in UInt8.random(in: 0...1) } print("generated random data: \(numbers)") let data = Data(numbers) let mutable = NSMutableData(data: data) try! mutable.compress(using: .lz4) print("compressed size: \(mutable.count)") try! mutable.write(toFile: "compressed.dat", options: [])
之后用Python的lz4库读取该文件并尝试解压:
import os import lz4.frame fh = open('compressed.dat', 'rb') ba = bytearray(fh.read()) print("read %d bytes" % len(ba)) decompressed = lz4.frame.decompress(ba) print("decompressed to %d bytes" % len(decompressed))
调用lz4.frame.decompress时出现错误:
RuntimeError: LZ4F_getFrameInfo failed with code: ERROR_frameType_unknown
尝试改用lz4.block.decompress时同样报错:
_block.LZ4BlockError: Decompression failed: corrupt input or insufficient space in destination buffer. Error code: 4
问题原因
Swift系统API NSMutableData.compress(using: .lz4)使用的是苹果自定义的LZ4封装格式,并非标准的LZ4 Frame或Block格式:
- 苹果会在原始LZ4压缩数据前添加一个8字节的自定义头部:前4字节是固定魔数
0x62767831(对应字节b'vbx1'),后4字节是小端存储的解压后数据长度。 - 标准LZ4库(包括Python的
lz4)无法识别这个自定义头部,因此直接解压会失败。
解决办法
方案1:Python端适配苹果的自定义格式
读取文件时先剥离头部,再用lz4.block.decompress并指定解压后长度,修改后的Python代码如下:
import lz4.block with open('compressed.dat', 'rb') as fh: # 读取并验证苹果LZ4魔数 magic_header = fh.read(4) if magic_header != b'\x62\x76\x78\x31': raise ValueError("文件不是苹果LZ4压缩格式") # 读取小端存储的解压后长度 uncompressed_len = int.from_bytes(fh.read(4), byteorder='little') # 读取实际的LZ4压缩块数据 compressed_block = fh.read() # 解压时指定目标长度,避免因数据随机性导致的解压错误 decompressed_data = lz4.block.decompress(compressed_block, uncompressed_size=uncompressed_len) print(f"解压后数据长度: {len(decompressed_data)}")
方案2:Swift端改用标准LZ4格式压缩
如果希望压缩数据能被标准LZ4工具/库直接识别,可以改用第三方LZ4库(比如LZ4Swift),而非系统的NSMutableData.compressAPI,确保生成标准的LZ4 Frame或Block格式数据。
内容的提问来源于stack exchange,提问作者Taylor
相关产品推荐
相关产品推荐

