GZIP文件格式解析和Inflate静态Huffman解压缩

GZIP是封装了Deflate压缩的格式文件;Deflate使用了无压缩、Huffman+LZ77进行压缩;解压是Inflate,Huffman包括静态Huffman压缩和动态Huffman压缩两种模式。

Java语言实现了GZIP格式解析、Inflate的静态Huffman解压缩、CRC32校验 算法。

gzip文件格式解析代码

	    BinaryInputStream bis = new BinaryInputStream(bytes); // 二进制字节流读取类
	
		Header header = new Header();  // 读取GZIP header
		header.ID1 = bis.ReadUInt8();  // GZIP ID1
		header.ID2 = bis.ReadUInt8();  // GzIP ID2
		header.CM = bis.ReadUInt8();  
		header.Flag = bis.ReadUInt8();
		header.Time = bis.ReadUnix32TimeStamp(); 
		header.Xfl = bis.ReadUInt8();
		header.OS = bis.ReadUInt8();
		
		gzip.header = header;
		
		Console.printlnf("ID=%xb%xb", header.ID1, header.ID2); // 打印
		Console.printlnf("CM=%xb", header.CM);
		Console.printlnf("Flag=%xb", header.Flag);
		Console.printlnf("MTime=%S", header.Time);
		Console.printlnf("XFL=%xb", header.Xfl);
		Console.printlnf("OS=%xb", header.OS);
		
		if ( (header.Flag & 0b00001000) != 0) {  // 如果标志位是文件名
			String filename = bis.ReadCString(); // 读取字节流until 0->char[]->string
			Console.printlnf("filename=%S", filename);
		}
		else // 其他待实现
			throw new java.lang.IllegalArgumentException(header.Flag +"");
		
		byte[] result ; // 解压后的字节
		int data_len ;  // 解压前数据长度
		int crc;        // 程序计算解压后的CRC32(见上篇文章)
		
		if (header.CM == 8) { // deflate 
			data_len = bytes.length - bis.GetPosition() - 8;			
			byte[] data = bis.ReadBytes(data_len); // 解压缩前数据	
			result = Deflate.uncompress(data);  // 解压缩,返回解压后字节流
			
			crc = CRC.CRC32(result); // 计算循环冗余码
		}
		else // 待实现
			throw new java.lang.IllegalArgumentException(header.CM +""); 
		
		gzip.crc = bis.ReadUInt32(); // gzip文件自身存储的crc32值
		gzip.isize = bis.ReadInt32(); 
		
		System.out.println("gzip crc="+ Long.toHexString(gzip.crc) +",calc-crc=" + Integer.toHexString( crc) );

解析结果如下:

显示了GZIP标志、压缩方法、压缩时间、原始文件名、操作系统类型、CRC校验值

GZIP中存储的CRC32值(gzip crc)==解压后计算的CRC32值(calc crc)。(0xa93145a2)

Inflate -静态Huffman解压缩:

	// 读取扩展Code
	private static int ReadExtCode(BitsInputStream bis, int len) {
		bis.setOrder(BitOrder.LeftIsHigh);
		return bis.ReadBits(len);
	}
	
	// 读取距离
	private static int ReadDistance(BitsInputStream bis) {
		bis.setOrder(BitOrder.RightIsHigh);
		return bis.ReadBits(5);
	}
	
	// 读取Code
	private static int ReadCode(BitsInputStream bis) {
		bis.setOrder(BitOrder.RightIsHigh);
		int code = bis.ReadBits(7);
		Integer value = FixHuffmanTable_7.get(code); // 7位长查表
		
		if (value == null) {
			int ext = bis.ReadBit();
			code = (code << 1 | ext);
			
			value = FixHuffmanTable_8.get(code); // 8位长查表
			
			if (value == null) {
				ext = bis.ReadBit();
				code = (code << 1 | ext);
				value = FixHuffmanTable_9.get(code); // 9位长查表
				
				if (value == -1)
					throw new java.lang.IllegalArgumentException(code + "");
			}
		}

		return value;
	}
	
	// Deflate解压缩
	public static byte[] uncompress(final byte[] _input) throws IOException {
		IntArrayBuffer baos = new IntArrayBuffer(); // 输出窗口
		
		// 位流读取类
		BitsInputStream bis = new BitsInputStream(_input);
		
		while (true) {
		    int bFinal = bis.ReadBits(1); // 读取Deflate头,0 – 还有后续子块;1 – 该子块是最后一块。
		    int bType = bis.ReadBits(2);  // 读取Deflate头,00 – 不压缩;01 – 静态Huffman编码压缩;10 – 动态Huffman编码压缩;11 – 保留

			if (bType == 0) { // 无压缩,未实现
				int len = bis.ReadBits(16);
				int nlen = bis.ReadBits(16);
				
				assert len + nlen == 65535;
				throw new java.lang.UnsupportedOperationException(bType + ""); 
			}
			else if (bType == 1) { // fixed Huffman
				while (true) {
					int value = ReadCode(bis); // 读取Huffman code
					
                    // 根据literal范围判断
					if (value >= 0 && value <= 255) // literal
						baos.Write(value);
					else if (value == 256) // 结束标志
						break ;
					else if (value >= 257 && value <= 285) { // 距离或长度
						int length = LengthExtraCodeLengthsTable.get(value);
						int bits = LengthExtraCodeBitsTable.get(value); 
						
						if (bits != 0) {
							int ext =  ReadExtCode(bis, bits); // 读取长度
							length = length + ext; 
						}
						
						value = ReadDistance(bis); // 读取距离
						int distance = DistanceExtraCodeLengthsTable.get(value);
						bits = DistanceExtraCodeBitsTable.get(value);
						
						if (bits != 0) {
							int ext =ReadExtCode(bis , bits); // 读取距离
							distance = distance + ext; 
						}
						
						// LZ77滑动窗口计算获取量
						int[] arr = baos.GetInts();
						int d = arr.length - distance;
						if (d < 0) {
							d = 0;
							length = length + distance - arr.length;
						}
						
						// 读取滑动窗口,写入到结果
						for (int i=0; i<length; i++) {	
							int m = arr[ d + i];
							
							baos.Write(m);
							arr = baos.GetInts();
						}
						
					}
					else 
						throw new java.lang.IllegalArgumentException(value+ "");

				}
			}
			else if (bType == 2) { // dynamic huffman 动态Huffman待实现
				throw new java.lang.UnsupportedOperationException(bType + "");
			}
			else 
				throw new java.lang.IllegalArgumentException(bType + "");
			
			if (bFinal == 1) // 最后一个块
				break ;
		}
		
        // 结果
		int[] b = baos.GetInts();
		byte[] result = new byte[b.length];
		for (int i=0; i<b.length; i++) 
			result[i] = (byte) b[i];
		
		return result;

测试结果如下:

将字符串"abcabcabcabcabcabcabcabcabcabcabcabc1111"写入到文件,

运行gzip程序(系统程序)压缩,

使用Java程序对gzip文件解析并显示解压后的内容。 (gzip使用了静态huffman)。

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
下面是一个简单的文件解压缩程序,使用 zlib 库实现: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <zlib.h> #define CHUNK_SIZE 1024 int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s <file.gz>\n", argv[0]); return 1; } char *infile = argv[1]; char outfile[256]; strncpy(outfile, infile, strlen(infile) - 3); outfile[strlen(infile) - 3] = '\0'; int ret; unsigned have; z_stream strm; unsigned char in[CHUNK_SIZE]; unsigned char out[CHUNK_SIZE]; FILE *f_in = fopen(infile, "rb"); if (!f_in) { printf("Failed to open input file %s\n", infile); return 1; } FILE *f_out = fopen(outfile, "wb"); if (!f_out) { printf("Failed to open output file %s\n", outfile); fclose(f_in); return 1; } memset(&strm, 0, sizeof(z_stream)); ret = inflateInit2(&strm, 15 + 32); if (ret != Z_OK) { printf("Failed to initialize inflate: %s\n", strm.msg); fclose(f_in); fclose(f_out); return 1; } do { strm.avail_in = fread(in, 1, CHUNK_SIZE, f_in); if (strm.avail_in == 0) { break; } strm.next_in = in; do { strm.avail_out = CHUNK_SIZE; strm.next_out = out; ret = inflate(&strm, Z_NO_FLUSH); switch (ret) { case Z_NEED_DICT: case Z_DATA_ERROR: case Z_MEM_ERROR: printf("Failed to inflate: %s\n", strm.msg); inflateEnd(&strm); fclose(f_in); fclose(f_out); return 1; } have = CHUNK_SIZE - strm.avail_out; fwrite(out, 1, have, f_out); } while (strm.avail_out == 0); } while (ret != Z_STREAM_END); inflateEnd(&strm); fclose(f_in); fclose(f_out); return 0; } ``` 程序的使用方法为: ``` $ ./unzip file.gz ``` 其中 `file.gz` 是要解压缩文件名。程序将解压缩后的文件保存在与输入文件同名的文件夹中,去除 `.gz` 后缀。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值