LZW压缩算法
来源:互联网 发布:北京游戏外包公司知乎 编辑:程序博客网 时间:2024/05/16 06:32
介绍
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
a,b,256,257,259。
解压的时候过程正好相反,根据码表,做码制与字符的替换输出就行了,具体细节可以参照我的代码实现。
算法代码实现:
输入源文件srcFile.txt:
- ababbabab
- 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;
- }
- }
- 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();
- }
- }
- }
- 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
算法的遗漏点
算法整体不是很难,仔细去想一般都能找到压缩的方式,就是在解压的过程中药考虑到编码前缀解析掉之后,他的编码前缀还可能是一个编码所以需要递归的解析,在这个测试例子中你可能没有看见预想到的压缩效果,那时因为文本量实在太小,就几个字节,当测试的文本达到几十k的时候,并且捏造的数据中出现大量的重复字符串时,压缩的效果就会显现出来。
LZW算法的特点
LZW压缩算法对于可预测性不大的数据压缩的效果会比较好,还有1个是时常出现重复的字符时,也可以比较好的压缩,还有是对于机器的硬件要求不太高。
- LZW压缩算法
- lzw压缩算法
- lzw 压缩算法源代码
- LZW压缩算法源代码
- lzw图像压缩算法
- LZW 压缩算法
- 压缩算法-LZW
- LZW压缩算法
- LZW压缩算法
- LZW压缩算法
- LZW压缩算法
- LZW压缩算法
- LZW压缩算法
- LZW算法压缩和解压缩
- LZW压缩算法js版
- 关于LZW压缩算法[转]
- LZW压缩算法js版
- lzw字符串压缩算法实现
- 欢迎使用CSDN-markdown编辑器
- 3192: [JLOI2013]删除物品
- redis初探
- EJB面试题
- 第11章 Camera
- LZW压缩算法
- CocoaPods安装Alamofire Xcode7.1
- Spring 配置log4j和简单介绍Log4J的使用
- 封闭iOS与开放Android的发展看移动未来方向
- 开发必会的 Linux & Mac 命令
- nginx如何响应客户端
- 攫取字符串
- 黑马程序员——面向对象_多态
- linux下如何处理文件名含括号的文件