LZW压缩算法

介绍

LZW算法是非常常见的一种压缩算法,他的压缩原理是对于多次重复出现的字符串,进行压缩,至于怎么压缩,在后文中会细细描述,LZW算法可以用在很多的场合,诸如图像压缩,文本压缩等等,而且算法简单易懂,并不是人们想象中的那么深奥。

算法原理

在介绍算法原理之前,得先明白几个概念:

1、Prefix,在这里代表前缀字符的意思。

2、Suffix,对应的意思是后缀字符的意思。

为什么提到这2个概念呢,是因为后面的字符的压缩的输入的过程就与这2者相关。这里假设压缩的是文本字符,字符内容如下:

ababbabab

测试的数据未必是必须多的,上面的字符中还是存在着一些重复的字符段的,可以满足题目的要求的。好,下面是压缩的流程:

1、从左往右逐一的读取源文件中的字符,构成前缀,后缀字符词组的方式。

2、如果构成的词组没有被编码过,则进行编码,并且输出此时的前缀字符,然后后缀字符替代前缀字符,后缀字符继续从文件中读入。

3、如果构成的词组被编码过,就是说这个词组之前出现过,是重复的,则不输出,将对应于此时词组的编码赋给词组的前缀,然后继续读入后缀字符。


第几步


前缀


后缀



存在对应码


输出



1


a


(,a)


2


a


b


(a,b)


no


a


256


3


b


a


(b,a)


no


b


257


4


a


b


(a,b)


yes


5


256


b


(256,b)


no


256


258


6


b


a


(b,a)


yes


7


257


b


(257,b)


no


257


259


8


b


a


(b,a)


yes


9


257


b


(257,b)


yes

             

上述的最后一步是在输入结束之后,最后将(257,b)变为259后输出,所以最后的输出为:

a,b,256,257,259。

解压的时候过程正好相反,根据码表,做码制与字符的替换输出就行了,具体细节可以参照我的代码实现。

算法代码实现:

输入源文件srcFile.txt:

ababbabab

词组类WordFix.java:

package LZW;

import java.util.HashMap;
import java.util.Map;

/**
 * 词组,包括前缀和后缀
 *
 * @author lyq
 *
 */
public class WordFix {
	// 词组前缀
	String prefix;
	// 词组后缀
	String suffix;

	// 编码词组映射表
	HashMap<WordFix, Integer> word2Code;

	public WordFix(String prefix, String suffix,
			HashMap<WordFix, Integer> word2Code) {
		this.prefix = prefix;
		this.suffix = suffix;
		this.word2Code = word2Code;
	}

	/**
	 * 设置前缀
	 *
	 * @param str
	 */
	public void setPrefix(String str) {
		this.prefix = str;
	}

	/**
	 * 设置后缀
	 *
	 * @param str
	 */
	public void setSuffix(String str) {
		this.suffix = str;
	}

	/**
	 * 获取前缀字符
	 *
	 * @return
	 */
	public String getPrefix() {
		return this.prefix;
	}

	/**
	 * 判断2个词组是否相等,比较前后字符是否相等
	 *
	 * @param wf
	 * @return
	 */
	public boolean isSame(WordFix wf) {
		boolean isSamed = true;

		if (!this.prefix.equals(wf.prefix)) {
			isSamed = false;
		}

		if (!this.suffix.equals(wf.suffix)) {
			isSamed = false;
		}

		return isSamed;
	}

	/**
	 * 判断此词组是否已经被编码
	 *
	 * @return
	 */
	public boolean hasWordCode() {
		boolean isContained = false;
		WordFix wf = null;

		for (Map.Entry entry : word2Code.entrySet()) {
			wf = (WordFix) entry.getKey();
			if (this.isSame(wf)) {
				isContained = true;
				break;
			}
		}

		return isContained;
	}

	/**
	 * 词组进行编码
	 *
	 * @param wordCode
	 *            此词组将要被编码的值
	 */
	public void wordFixCoded(int wordCode) {
		word2Code.put(this, wordCode);
	}

	/**
	 * 读入后缀字符
	 *
	 * @param str
	 */
	public void readSuffix(String str) {
		int code = 0;
		boolean isCoded = false;
		WordFix wf = null;

		for (Map.Entry entry : word2Code.entrySet()) {
			code = (int) entry.getValue();
			wf = (WordFix) entry.getKey();
			if (this.isSame(wf)) {
				isCoded = true;
				// 编码变为前缀
				this.prefix = code + "";
				break;
			}
		}

		if (!isCoded) {
			return;
		}
		this.suffix = str;
	}

	/**
	 * 将词组转为连续的字符形式
	 *
	 * @return
	 */
	public String transToStr() {
		int code = 0;
		String currentPrefix = this.prefix;

		for(Map.Entry entry: word2Code.entrySet()){
			code = (int) entry.getValue();
			//如果前缀字符还是编码,继续解析
			if(currentPrefix.equals(code + "")){
				currentPrefix =((WordFix) entry.getKey()).transToStr();
				break;
			}
		}

		return currentPrefix + this.suffix;
	}

}

压缩算法工具类LZWTool.java:

package LZW;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * LZW解压缩算法工具类
 *
 * @author lyq
 *
 */
public class LZWTool {
	// 开始的编码的编码号从256开始
	public static int LZW_CODED_NUM = 256;

	// 待压缩文件地址
	private String srcFilePath;
	// 目标文件地址
	private String desFileLoc;
	// 压缩后的目标文件名
	private String desFileName;
	// 结果字符,将被写到输出文件中
	private String resultStr;
	// 编码词组映射表
	HashMap<WordFix, Integer> word2Code;
	// 源文件数据
	private ArrayList<String> totalDatas;

	public LZWTool(String srcFilePath, String desFileLoc, String desFileName) {
		this.srcFilePath = srcFilePath;
		this.desFileLoc = desFileLoc;
		this.desFileName = desFileName;

		word2Code = new HashMap<>();
		totalDatas = new ArrayList<>();
		readDataFile(totalDatas);
	}

	/**
	 * 从文件中读取数据
	 *
	 * @param inputData
	 *            输入数据容器
	 */
	private void readDataFile(ArrayList<String> inputData) {
		File file = new File(srcFilePath);
		ArrayList<String[]> dataArray = new ArrayList<String[]>();

		try {
			BufferedReader in = new BufferedReader(new FileReader(file));
			String str;
			String[] tempArray;
			while ((str = in.readLine()) != null) {
				tempArray = new String[str.length()];
				for (int i = 0; i < str.length(); i++) {
					tempArray[i] = str.charAt(i) + "";
				}

				dataArray.add(tempArray);
			}
			in.close();
		} catch (IOException e) {
			e.getStackTrace();
		}

		System.out.print("压缩前的字符:");
		for (String[] array : dataArray) {
			for (String s : array) {
				inputData.add(s);
				System.out.print(s);
			}
		}
		System.out.println();
	}

	/**
	 * 进行lzw压缩
	 */
	public void compress() {
		resultStr = "";
		boolean existCoded = false;
		String prefix = totalDatas.get(0);
		WordFix wf = null;

		for (int i = 1; i < totalDatas.size(); i++) {
			wf = new WordFix(prefix, totalDatas.get(i), word2Code);
			existCoded = false;

			// 如果当前词组存在相应编码,则继续读入后缀
			while (wf.hasWordCode()) {
				i++;
				// 如果到底了则跳出循环
				if (i == totalDatas.size()) {
					// 说明还存在词组编码的
					existCoded = true;
					wf.readSuffix("");
					break;
				}

				wf.readSuffix(totalDatas.get(i));
			}

			if (!existCoded) {
				// 对未编码过的词组进行编码
				wf.wordFixCoded(LZW_CODED_NUM);
				LZW_CODED_NUM++;
			}

			// 将前缀输出
			resultStr += wf.getPrefix() + ",";
			// 后缀边前缀
			prefix = wf.suffix;
		}

		// 将原词组的后缀加入也就是新的词组的前缀
		resultStr += prefix;
		System.out.println("压缩后的字符:" + resultStr);
		writeStringToFile(resultStr, desFileLoc + desFileName);
	}

	public void unCompress(String srcFilePath, String desFilePath) {
		String result = "";
		int code = 0;

		File file = new File(srcFilePath);
		ArrayList<String[]> datas = new ArrayList<String[]>();

		try {
			BufferedReader in = new BufferedReader(new FileReader(file));
			String str;
			String[] tempArray;
			while ((str = in.readLine()) != null) {
				tempArray = str.split(",");
				datas.add(tempArray);
			}
			in.close();
		} catch (IOException e) {
			e.getStackTrace();
		}

		for (String[] array : datas) {
			for (String s : array) {
				for (Map.Entry entry : word2Code.entrySet()) {
					code = (int) entry.getValue();
					if (s.equals(code + "")) {
						s = ((WordFix) entry.getKey()).transToStr();
						break;
					}
				}

				result += s;
			}
		}

		System.out.println("解压后的字符:" + result);
		writeStringToFile(result, desFilePath);
	}

	/**
	 * 写字符串到目标文件中
	 *
	 * @param resultStr
	 */
	public void writeStringToFile(String resultStr, String desFilePath) {
		try {
			File file = new File(desFilePath);
			PrintStream ps = new PrintStream(new FileOutputStream(file));
			ps.println(resultStr);// 往文件里写入字符串
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

测试调用类Client.java:

package LZW;

/**
 * LZW解压缩算法
 * @author lyq
 *
 */
public class Client {
	public static void main(String[] args){
		//源文件地址
		String srcFilePath = "C:\\Users\\lyq\\Desktop\\icon\\srcFile.txt";
		//压缩后的文件名
		String desFileName = "compressedFile.txt";
		//压缩文件的位置
		String desFileLoc = "C:\\Users\\lyq\\Desktop\\icon\\";
		//解压后的文件名
		String unCompressedFilePath = "C:\\Users\\lyq\\Desktop\\icon\\unCompressedFile.txt";

		LZWTool tool = new LZWTool(srcFilePath, desFileLoc, desFileName);
		//压缩文件
		tool.compress();

		//解压文件
		tool.unCompress(desFileLoc + desFileName, unCompressedFilePath);
	}
}

结果输出:

压缩前的字符:ababbabab
压缩后的字符:a,b,256,257,259,
解压后的字符:ababbabab

在文件目录中的3个文件显示:

算法的遗漏点

算法整体不是很难,仔细去想一般都能找到压缩的方式,就是在解压的过程中药考虑到编码前缀解析掉之后,他的编码前缀还可能是一个编码所以需要递归的解析,在这个测试例子中你可能没有看见预想到的压缩效果,那时因为文本量实在太小,就几个字节,当测试的文本达到几十k的时候,并且捏造的数据中出现大量的重复字符串时,压缩的效果就会显现出来。

LZW算法的特点

LZW压缩算法对于可预测性不大的数据压缩的效果会比较好,还有1个是时常出现重复的字符时,也可以比较好的压缩,还有是对于机器的硬件要求不太高。

时间: 2024-10-12 18:55:30

LZW压缩算法的相关文章

LZW压缩算法——简明原理与实现

LZW和哈夫曼编码一样,是无损压缩中的一种.该算法通过建立字典,实现字符重用与编码,适用于source中重复率很高的文本压缩.本文首先讲下LZW的编解码原理,然后给出LZW的实现code. *********************原理********************* 编码: 编码0-255用来存储Ascii码为[0,255]的字符,放在字典里. 编码从256开始,将出现过的字符计入字典 核心思想:利用字符的可重用性,每当往结果输出一个编码,就将一个新的string存入dictiona

【数据压缩】LZW算法原理与源码解析

转载请注明出处:http://blog.csdn.net/luoshixian099/article/details/50331883 <勿在浮沙筑高台> LZW压缩算法原理非常简单,因而被广泛地采用,已经被引入主流图像文件格式中.该算法由Lempel-Ziv-Welch三人发明,这种技术将定长码字分配给变长信源符号序列,它不需要知道被压缩文件的符号出现概率的先验知识,只需要动态地建立和维护一个字典,和其他压缩算法相比既是缺点也是优点. 1. LZW原理 1.1 概念的理解 LZW通过建立一个

【手打】LZW编码的C/C++实现

LZW编码通过建立一个字符串表,用较短的代码来表示较长的字符串来实现压缩. LZW压缩算法是Unisys的专利,有效期到2003年,所以相关算法大多也已过期. 本代码仅仅完成了LZW的编码与解码算法功能,相对网上找到的很多代码而言较为简(cai)单(bi),了解struct && 会递归即可,算是优点吧. #include <stdio.h> #include <algorithm> #include <math.h> #include <iost

【数据压缩】LZW算法原理与源代码解析

转载请注明出处:http://blog.csdn.net/luoshixian099/article/details/50331883 <勿在浮沙筑高台> LZW压缩算法原理很easy,因而被广泛地採用,已经被引入主流图像文件格式中. 该算法由Lempel-Ziv-Welch三人发明,这样的技术将定长码字分配给变长信源符号序列,它不须要知道被压缩文件的符号出现概率的先验知识,仅仅须要动态地建立和维护一个字典,和其它压缩算法相比既是缺点也是长处. 1. LZW原理 1.1 概念的理解 LZW通过

【转】gif文件格式详解

1.概述 ~~~~~~~~ GIF(Graphics Interchange Format,图形交换格式)文件是由 CompuServe公司开发的图形文件格式,版权所有,任何商业目的使用均须 CompuServe公司授权. GIF图象是基于颜色列表的(存储的数据是该点的颜色对应于颜色列表的索引值),最多只支持8位(256色).GIF文件内部分成许多存储块,用来存 储多幅图象或者是决定图象表现行为的控制块,用以实现动画和交互式应用.GIF文件还通过LZW压缩算法压缩图象数据来减少图象尺寸(关于LZ

GDI+编程小结

GDI+(Graphics Device Interface Plus图形设备接口加)是Windows XP和Windows Server 2003操作系统的子系统,也是.NET框架的重要组成部分,负责在屏幕和打印机上绘制图形图像和显示信息. GDI+不但在功能上比GDI 要强大很多,而且在代码编写方面也更简单,因此会很快成为Windows图形图像程序开发的首选. 一.              GDI+的特点和新增功能 GDI+与GDI一样,都具有设备无关性.应用程序的程序员可利用GDI+这样

浓缩的才是精华:浅析GIF格式图片的存储和压缩

GIF(Graphics Interchange Format)原义是"图像互换格式",是CompuServe公司在1987年开发出的图像文件格式,可以说是互联网界的老古董了. GIF格式可以存储多幅彩色图像,如果将这些图像连续播放出来,就能够组成最简单的动画.所以常被用来存储"动态图片",通常时间短,体积小,内容简单,成像相对清晰,适于在早起的慢速互联网上传播. 本来,随着网络带宽的拓展和视频技术的进步,这种图像已经渐渐失去了市场.可是,近年来流行的表情包文化,让

图像GIF格式介绍

1 图像GIF格式工作原理 GIF是用于压缩具有单调颜色和清晰细节的图像(如线状图.徽标或带文字的插图)的标准格式. GIF(Graphics InterchangeFormat)的原义是“图像互换格式”,是CompuServe公司在1987年开发的图像文件格式.GIF文件的数据,是一种基于LZW算法的连续色调的无损压缩格式.其压缩率一般在50%左右,它不属于任何应用程序.目前几乎所有相关软件都支持它,公共领域有大量的软件在使用GIF图像文件.GIF图像文件的数据是经过压缩的,而且是采用了可变长

GIF/PNG/JPG和WEBP图片有点和缺点整理

GIF/PNG/JPG/WEBP都是属于位图(位图 ,务必区别于矢量图): GIF/PNG和JPG这三种格式的图片被广泛应用在现今的互联网中,gif曾在过去互联网初期慢速的情况下几乎是做到了大一统的地位,而现如今随着互联网技术应用和硬件条件的提高,png和jpg格式的图片越来越多的被应用,gif昔日的辉煌一去不复, webp图片格式现在还不普及:  GIF(Graphics Interchange Format) GIF图形交换格式是一种位图图形文件格式,以8位色(即256种颜色)重现真彩色的图