Ang data compression gamit ang Huffman algorithm

entry

Niini nga artikulo, maghisgot ako bahin sa iladong Huffman algorithm, ingon man ang aplikasyon niini sa data compression.

Ingon usa ka sangputanan, magsulat kami usa ka yano nga archiver. Kini nahimo na artikulo sa Habré, apan walay praktikal nga pagpatuman. Ang teoretikal nga materyal sa kasamtangan nga post gikuha gikan sa mga leksyon sa computer science sa eskwelahan ug sa libro ni Robert Laforet nga "Data Structures and Algorithms in Java". Busa, ang tanan ubos sa pagputol!

Usa ka gamay nga pagpamalandong

Sa usa ka normal nga text file, ang usa ka karakter gi-encode sa 8 bits (ASCII encoding) o 16 (Unicode encoding). Sunod, atong tagdon ang ASCII encoding. Pananglitan, kuhaa ang kuwerdas s1 = "SAYSAY NI SUSIE NGA DALI N". Sa kinatibuk-an, adunay 22 nga mga karakter sa linya, siyempre, lakip ang mga luna ug ang bag-ong linya nga karakter - 'n'. Ang file nga naglangkob niini nga linya motimbang 22*8 = 176 bits. Ang pangutana mitungha dayon: makatarunganon ba nga gamiton ang tanan nga 8 bits aron ma-encode ang 1 nga karakter? Wala namo gamita ang tanang ASCII nga mga karakter. Bisan kung sila, mas makataronganon ang paghatag sa labing kanunay nga letra - S - ang labing kadali nga posible nga code, ug alang sa labing talagsaon nga letra - T (o U, o 'n') - hatagan ang code nga labi ka tinuod. Kini ang algorithm sa Huffman: kinahanglan nimo pangitaon ang labing kaayo nga kapilian sa pag-encode, diin ang file adunay labing gamay nga gibug-aton. Normal ra nga ang lainlaing mga karakter adunay lainlaing mga gitas-on sa code - kini ang sukaranan sa algorithm.

Pag-coding

Ngano nga dili hatagan ang karakter nga 'S' usa ka code, pananglitan, 1 ka gamay ang gitas-on: 0 o 1. Himoa nga 1. Unya ang ikaduha nga labing nahitabo nga karakter - ' ' (space) - hatagan namon ang 0. Hunahunaa, nagsugod ka sa decode sa imong mensahe - encoded string s1 - ug imong makita nga ang code nagsugod sa 1. Busa, unsay buhaton: kini ba ang karakter S, o kini ba sa uban nga mga karakter, sama sa A? Busa, usa ka importante nga lagda ang mitungha:

Walay code kinahanglang prefix sa lain

Kini nga lagda mao ang yawe sa algorithm. Busa, ang paghimo sa code nagsugod sa usa ka frequency table, nga nagpaila sa frequency (gidaghanon sa mga panghitabo) sa matag simbolo:

Ang data compression gamit ang Huffman algorithm Ang mga karakter nga adunay labing kadaghan nga panghitabo kinahanglan nga i-encode sa labing gamay mahimo ang gidaghanon sa mga bit. Maghatag ako usa ka pananglitan sa usa sa posible nga mga lamesa sa code:

Ang data compression gamit ang Huffman algorithm Busa ang gi-encode nga mensahe mahimong sama niini:

10 01111 10 110 1111 00 10 010 1110 10 00 110 0110 00 110 10 00 1111 010 10 1110 01110

Gilain nako ang code sa matag karakter nga adunay usa ka espasyo. Dili kini mahitabo sa usa ka compressed file!
Ang pangutana mitungha: giunsa kini nga rookie nga adunay usa ka code kung giunsa paghimo ang usa ka lamesa sa code? Kini hisgotan sa ubos.

Pagtukod ug Huffman Tree

Dinhi diin ang mga punoan sa pagpangita sa binary moabut aron sa pagluwas. Ayaw kabalaka, dili nimo kinahanglan ang pagpangita, pagsulud, ug pagtangtang sa mga pamaagi dinhi. Ania ang istruktura sa kahoy sa java:

public class Node {
    private int frequence;
    private char letter;
    private Node leftChild;
    private Node rightChild;
    ...
}

class BinaryTree {
    private Node root;

    public BinaryTree() {
        root = new Node();
    }
    public BinaryTree(Node root) {
        this.root = root;
    }
    ...
}

Dili kini ang kompleto nga code, ang tibuuk nga code naa sa ubos.

Ania ang algorithm sa paghimo sa usa ka kahoy:

  1. Paghimo og Node nga butang alang sa matag karakter gikan sa mensahe (linya s1). Sa among kaso, adunay 9 ka mga node (mga butang sa Node). Ang matag node naglangkob sa duha ka data field: simbolo ug frequency
  2. Paghimo usa ka butang nga Kahoy (BinaryTree) alang sa matag usa sa mga node sa Node. Ang node nahimong gamot sa kahoy.
  3. Isulod kini nga mga kahoy sa priority queue. Kon mas ubos ang frequency, mas taas ang prayoridad. Mao nga, kung magkuha, ang kahoy nga adunay labing ubos nga frequency kanunay gipili.

Sunod, kinahanglan nimo nga cyclically buhaton ang mosunod:

  1. Kuhaa ang duha ka kahoy gikan sa priority queue ug himoa sila nga mga anak sa usa ka bag-ong node (usa ka bag-ong nahimo nga node nga walay sulat). Ang kadaghanon sa bag-ong node parehas sa kadaghanon sa mga frequency sa duha ka punoan nga punoan.
  2. Alang niini nga node, paghimo og usa ka kahoy nga nakagamot niini nga node. Isulod kini nga kahoy balik sa priority queue. (Tungod kay ang kahoy adunay bag-o nga frequency, kini lagmit nga mosulod sa usa ka bag-ong lugar sa pila)
  3. Ipadayon ang mga lakang 1 ug 2 hangtod nga usa ka kahoy ang nahabilin sa pila - ang kahoy nga Huffman

Hunahunaa kini nga algorithm sa linya s1:

Ang data compression gamit ang Huffman algorithm

Dinhi ang simbolo nga "lf" (linefeed) nagpasabut sa usa ka bag-ong linya, "sp" (space) usa ka wanang.

Unsay sunod?

Nakuha namo ang kahoy nga Huffman. OK ra. Ug unsay buhaton niini? Dili nila kini kuhaon nga libre.Ug unya, kinahanglan nimo nga masubay ang tanan nga posible nga mga agianan gikan sa gamut hangtod sa mga dahon sa kahoy. Kami mouyon nga markahan ang usa ka sidsid nga 0 kung kini padulong sa wala nga bata ug 1 kung kini padulong sa tuo. Sa estrikto nga pagkasulti, sa kini nga mga notasyon, ang code sa karakter mao ang agianan gikan sa gamut sa kahoy hangtod sa dahon nga adunay kini nga kinaiya.

Ang data compression gamit ang Huffman algorithm

Sa ingon, ang lamesa sa mga kodigo nahimo. Timan-i nga kung atong tagdon kini nga lamesa, makahinapos kita mahitungod sa "kabug-at" sa matag karakter - kini ang gitas-on sa code niini. Dayon, sa compressed form, ang source file motimbang: 2 * 3 + 2 * 4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 bits . Sa sinugdan kini mitimbang ug 176 ka bit. Busa, gikunhoran namo kini sa 176/65 = 2.7 ka beses! Apan kini usa ka utopia. Ang ingon nga ratio lagmit dili makuha. Ngano man? Kini hisgotan sa ulahi.

Pag-decode

Aw, tingali ang pinakasimple nga butang nga nahabilin mao ang pag-decode. Sa akong hunahuna daghan kaninyo ang nakatag-an nga imposible ang paghimo lamang og usa ka compressed file nga walay bisan unsa nga mga pahiwatig kung giunsa kini pag-encode - dili namo kini ma-decode! Oo, oo, lisud alang kanako nga makaamgo niini, apan kinahanglan kong maghimo usa ka text file table.txt nga adunay usa ka compression table:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

Ang entry sa lamesa sa porma nga 'karakter' "kodigo sa karakter". Nganong walay simbolo ang 01110? Sa tinuud, kini adunay usa ka simbolo, ang mga gamit sa java nga akong gigamit sa pag-output sa usa ka file, ang karakter nga bag-ong linya - 'n' - gibag-o sa usa ka bag-ong linya (bisan kung unsa kini ka hungog). Busa, ang walay sulod nga linya sa ibabaw mao ang karakter para sa code 01110. Para sa code 00, ang karakter usa ka luna sa sinugdanan sa linya. Kinahanglan kong isulti dayon nga kini nga pamaagi sa pagtipig sa lamesa mahimong maangkon nga labing dili makatarunganon alang sa among khan coefficient. Apan kini sayon ​​sabton ug ipatuman. Malipay ako nga madungog ang imong mga rekomendasyon sa mga komento bahin sa pag-optimize.

Uban niini nga lamesa, kini sayon ​​kaayo sa pag-decode. Atong hinumdoman kung unsa nga lagda ang atong gigiyahan sa paghimo sa encoding:

Walay code kinahanglang prefix sa lain

Dinhi kini adunay papel sa pagpadali. Gibasa namo ang hinay-hinay nga sunod-sunod, ug sa diha nga ang resulta nga string d, nga naglangkob sa mga read bits, motakdo sa pag-encode nga katumbas sa karakter nga karakter, nahibal-an dayon namo nga ang karakter nga karakter (ug kini lamang!) ang na-encode. Sunod, among isulat ang karakter sa decode string (ang string nga adunay decoded nga mensahe), i-reset ang d string, ug basaha pa ang na-encode nga file.

Pagpatuman

Panahon na aron pakaulawan ang akong code pinaagi sa pagsulat sa usa ka archiver. Tawgon nato ni nga Compressor.

Pagsugod na usab. Una sa tanan, gisulat namon ang klase sa Node:

public class Node {
    private int frequence;//частота
    private char letter;//буква
    private Node leftChild;//левый потомок
    private Node rightChild;//правый потомок

   

    public Node(char letter, int frequence) { //собственно, конструктор
        this.letter = letter;
        this.frequence = frequence;
    }

    public Node() {}//перегрузка конструтора для безымянных узлов(см. выше в разделе о построении дерева Хаффмана)
    public void addChild(Node newNode) {//добавить потомка
        if (leftChild == null)//если левый пустой=> правый тоже=> добавляем в левый
            leftChild = newNode;
        else {
            if (leftChild.getFrequence() <= newNode.getFrequence()) //в общем, левым потомком
                rightChild = newNode;//станет тот, у кого меньше частота
            else {
                rightChild = leftChild;
                leftChild = newNode;
            }
        }

        frequence += newNode.getFrequence();//итоговая частота
    }

    public Node getLeftChild() {
        return leftChild;
    }

    public Node getRightChild() {
        return rightChild;
    }

    public int getFrequence() {
        return frequence;
    }

    public char getLetter() {
        return letter;
    }

    public boolean isLeaf() {//проверка на лист
        return leftChild == null && rightChild == null;
    }
}

Karon ang kahoy:

class BinaryTree {
    private Node root;

    public BinaryTree() {
        root = new Node();
    }

    public BinaryTree(Node root) {
        this.root = root;
    }

    public int getFrequence() {
        return root.getFrequence();
    }

    public Node getRoot() {
        return root;
    }
}

Prioridad nga pila:

import java.util.ArrayList;//да-да, очередь будет на базе списка

class PriorityQueue {
    private ArrayList<BinaryTree> data;//список очереди
    private int nElems;//кол-во элементов в очереди

    public PriorityQueue() {
        data = new ArrayList<BinaryTree>();
        nElems = 0;
    }

    public void insert(BinaryTree newTree) {//вставка
        if (nElems == 0)
            data.add(newTree);
        else {
            for (int i = 0; i < nElems; i++) {
                if (data.get(i).getFrequence() > newTree.getFrequence()) {//если частота вставляемого дерева меньше 
                    data.add(i, newTree);//чем част. текущего, то cдвигаем все деревья на позициях справа на 1 ячейку                   
                    break;//затем ставим новое дерево на позицию текущего
                }
                if (i == nElems - 1) 
                    data.add(newTree);
            }
        }
        nElems++;//увеличиваем кол-во элементов на 1
    }

    public BinaryTree remove() {//удаление из очереди
        BinaryTree tmp = data.get(0);//копируем удаляемый элемент
        data.remove(0);//собственно, удаляем
        nElems--;//уменьшаем кол-во элементов на 1
        return tmp;//возвращаем удаленный элемент(элемент с наименьшей частотой)
    }
}

Ang klase nga nagmugna sa kahoy nga Huffman:

public class HuffmanTree {
    private final byte ENCODING_TABLE_SIZE = 127;//длина кодировочной таблицы
    private String myString;//сообщение
    private BinaryTree huffmanTree;//дерево Хаффмана
    private int[] freqArray;//частотная таблица
    private String[] encodingArray;//кодировочная таблица


    //----------------constructor----------------------
    public HuffmanTree(String newString) {
        myString = newString;

        freqArray = new int[ENCODING_TABLE_SIZE];
        fillFrequenceArray();

        huffmanTree = getHuffmanTree();

        encodingArray = new String[ENCODING_TABLE_SIZE];
        fillEncodingArray(huffmanTree.getRoot(), "", "");
    }

    //--------------------frequence array------------------------
    private void fillFrequenceArray() {
        for (int i = 0; i < myString.length(); i++) {
            freqArray[(int)myString.charAt(i)]++;
        }
    }

    public int[] getFrequenceArray() {
        return freqArray;
    }

    //------------------------huffman tree creation------------------
    private BinaryTree getHuffmanTree() {
        PriorityQueue pq = new PriorityQueue();
        //алгоритм описан выше
        for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
            if (freqArray[i] != 0) {//если символ существует в строке
                Node newNode = new Node((char) i, freqArray[i]);//то создать для него Node
                BinaryTree newTree = new BinaryTree(newNode);//а для Node создать BinaryTree
                pq.insert(newTree);//вставить в очередь
            }
        }

        while (true) {
            BinaryTree tree1 = pq.remove();//извлечь из очереди первое дерево.

            try {
                BinaryTree tree2 = pq.remove();//извлечь из очереди второе дерево

                Node newNode = new Node();//создать новый Node
                newNode.addChild(tree1.getRoot());//сделать его потомками два извлеченных дерева
                newNode.addChild(tree2.getRoot());

                pq.insert(new BinaryTree(newNode);
            } catch (IndexOutOfBoundsException e) {//осталось одно дерево в очереди
                return tree1;
            }
        }
    }

    public BinaryTree getTree() {
        return huffmanTree;
    }

    //-------------------encoding array------------------
    void fillEncodingArray(Node node, String codeBefore, String direction) {//заполнить кодировочную таблицу
        if (node.isLeaf()) {
            encodingArray[(int)node.getLetter()] = codeBefore + direction;
        } else {
            fillEncodingArray(node.getLeftChild(), codeBefore + direction, "0");
            fillEncodingArray(node.getRightChild(), codeBefore + direction, "1");
        }
    }

    String[] getEncodingArray() {
        return encodingArray;
    }

    public void displayEncodingArray() {//для отладки
        fillEncodingArray(huffmanTree.getRoot(), "", "");

        System.out.println("======================Encoding table====================");
        for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
            if (freqArray[i] != 0) {
                System.out.print((char)i + " ");
                System.out.println(encodingArray[i]);
            }
        }
        System.out.println("========================================================");
    }
    //-----------------------------------------------------
    String getOriginalString() {
        return myString;
    }
}

Ang klase nga adunay sulod nga nag-encode/nag-decode:

public class HuffmanOperator {
    private final byte ENCODING_TABLE_SIZE = 127;//длина таблицы
    private HuffmanTree mainHuffmanTree;//дерево Хаффмана (используется только для сжатия)
    private String myString;//исходное сообщение
    private int[] freqArray;//частотаная таблица
    private String[] encodingArray;//кодировочная таблица
    private double ratio;//коэффициент сжатия 


    public HuffmanOperator(HuffmanTree MainHuffmanTree) {//for compress
        this.mainHuffmanTree = MainHuffmanTree;

        myString = mainHuffmanTree.getOriginalString();

        encodingArray = mainHuffmanTree.getEncodingArray();

        freqArray = mainHuffmanTree.getFrequenceArray();
    }

    public HuffmanOperator() {}//for extract;

    //---------------------------------------compression-----------------------------------------------------------
    private String getCompressedString() {
        String compressed = "";
        String intermidiate = "";//промежуточная строка(без добавочных нулей)
        //System.out.println("=============================Compression=======================");
        //displayEncodingArray();
        for (int i = 0; i < myString.length(); i++) {
            intermidiate += encodingArray[myString.charAt(i)];
        }
        //Мы не можем писать бит в файл. Поэтому нужно сделать длину сообщения кратной 8=>
        //нужно добавить нули в конец(можно 1, нет разницы)
        byte counter = 0;//количество добавленных в конец нулей (байта в полне хватит: 0<=counter<8<127)
        for (int length = intermidiate.length(), delta = 8 - length % 8; 
        		counter < delta ; counter++) {//delta - количество добавленных нулей
            intermidiate += "0";
        }
        
        //склеить кол-во добавочных нулей в бинарном предаствлении и промежуточную строку 
        compressed = String.format("%8s", Integer.toBinaryString(counter & 0xff)).replace(" ", "0") + intermidiate;
        		
        //идеализированный коэффициент
        setCompressionRatio();
        //System.out.println("===============================================================");
        return compressed;
    }
    
    private void setCompressionRatio() {//посчитать идеализированный коэффициент 
        double sumA = 0, sumB = 0;//A-the original sum
        for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
            if (freqArray[i] != 0) {
                sumA += 8 * freqArray[i];
                sumB += encodingArray[i].length() * freqArray[i];
            }
        }
        ratio = sumA / sumB;
    }

    public byte[] getBytedMsg() {//final compression
        StringBuilder compressedString = new StringBuilder(getCompressedString());
        byte[] compressedBytes = new byte[compressedString.length() / 8];
        for (int i = 0; i < compressedBytes.length; i++) {
                compressedBytes[i] = (byte) Integer.parseInt(compressedString.substring(i * 8, (i + 1) * 8), 2);
        }
        return compressedBytes;
    }
    //---------------------------------------end of compression----------------------------------------------------------------
    //------------------------------------------------------------extract-----------------------------------------------------
    public String extract(String compressed, String[] newEncodingArray) {
        String decompressed = "";
        String current = "";
        String delta = "";
        encodingArray = newEncodingArray;
        
        //displayEncodingArray();
        //получить кол-во вставленных нулей
        for (int i = 0; i < 8; i++) 
        	delta += compressed.charAt(i);
        int ADDED_ZEROES = Integer.parseInt(delta, 2);
       
        for (int i = 8, l = compressed.length() - ADDED_ZEROES; i < l; i++) {
            //i = 8, т.к. первым байтом у нас идет кол-во вставленных нулей
            current += compressed.charAt(i);
            for (int j = 0; j < ENCODING_TABLE_SIZE; j++) {
                if (current.equals(encodingArray[j])) {//если совпало
                    decompressed += (char)j;//то добавляем элемент
                    current = "";//и обнуляем текущую строку
                }
            }
        }

        return decompressed;
    }

    public String getEncodingTable() {
        String enc = "";
    	for (int i = 0; i < encodingArray.length; i++) {
        	if (freqArray[i] != 0) 
        		enc += (char)i + encodingArray[i] + 'n';
        }
    	return enc;
    }

    public double getCompressionRatio() {
        return ratio;
    }


    public void displayEncodingArray() {//для отладки
        System.out.println("======================Encoding table====================");
        for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
            //if (freqArray[i] != 0) {
                System.out.print((char)i + " ");
                System.out.println(encodingArray[i]);
            //}
        }
        System.out.println("========================================================");
    }
    }

Usa ka klase nga nagpadali sa pagsulat sa usa ka file:

import java.io.File;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Closeable;

public class FileOutputHelper implements Closeable {
    private File outputFile;
    private FileOutputStream fileOutputStream;

    public FileOutputHelper(File file) throws FileNotFoundException {
        outputFile = file;
        fileOutputStream = new FileOutputStream(outputFile);
    }

    public void writeByte(byte msg) throws IOException {
        fileOutputStream.write(msg);
    }

    public void writeBytes(byte[] msg) throws IOException {
        fileOutputStream.write(msg);
    }

    public void writeString(String msg) {
    	try (PrintWriter pw = new PrintWriter(outputFile)) {
    		pw.write(msg);
    	} catch (FileNotFoundException e) {
    		System.out.println("Неверный путь, или такого файла не существует!");
    	}
    }

    @Override
    public void close() throws IOException {
        fileOutputStream.close();
    }

    public void finalize() throws IOException {
        close();
    }
}

Usa ka klase nga nagpadali sa pagbasa gikan sa usa ka file:

import java.io.FileInputStream;
import java.io.EOFException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;

public class FileInputHelper implements Closeable {
	private FileInputStream fileInputStream;
	private BufferedReader fileBufferedReader;
	
	public FileInputHelper(File file) throws IOException {
		fileInputStream = new FileInputStream(file);
		fileBufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
	}
	
	
    public byte readByte() throws IOException {
    	int cur = fileInputStream.read();
    	if (cur == -1)//если закончился файл
    		throw new EOFException();
    	return (byte)cur;
    }
    
    public String readLine() throws IOException {
    	return fileBufferedReader.readLine();
    }
    
    @Override
    public void close() throws IOException{
    	fileInputStream.close();
    }
}

Aw, ug ang panguna nga klase:

import java.io.File;
import java.nio.charset.MalformedInputException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.List;
import java.io.EOFException;
public class Main {
	private static final byte ENCODING_TABLE_SIZE = 127;
	
    public static void main(String[] args) throws IOException {
        try {//указываем инструкцию с помощью аргументов командной строки
            if (args[0].equals("--compress") || args[0].equals("-c"))
                compress(args[1]);
            else if ((args[0].equals("--extract") || args[0].equals("-x"))
            		&& (args[2].equals("--table") || args[2].equals("-t"))) {
            	extract(args[1], args[3]);
            }
            else
                throw new IllegalArgumentException();
        } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
            System.out.println("Неверный формат ввода аргументов ");
            System.out.println("Читайте Readme.txt");
            e.printStackTrace();
        }
    }

	public static void compress(String stringPath) throws IOException {
        List<String> stringList;
        File inputFile = new File(stringPath);
        String s = "";
        File compressedFile, table;
        
        try {
            stringList = Files.readAllLines(Paths.get(inputFile.getAbsolutePath()));
        } catch (NoSuchFileException e) {
            System.out.println("Неверный путь, или такого файла не существует!");
            return;
        } catch (MalformedInputException e) {
        	System.out.println("Текущая кодировка файла не поддерживается");
        	return;
        }

        for (String item : stringList) {
            s += item;
            s += 'n';
        }

        HuffmanOperator operator = new HuffmanOperator(new HuffmanTree(s));

        compressedFile = new File(inputFile.getAbsolutePath() + ".cpr");
        compressedFile.createNewFile();
        try (FileOutputHelper fo = new FileOutputHelper(compressedFile)) {
        	fo.writeBytes(operator.getBytedMsg());
        }
        //create file with encoding table:
        
        table = new File(inputFile.getAbsolutePath() + ".table.txt");
        table.createNewFile();
        try (FileOutputHelper fo = new FileOutputHelper(table)) {
        	fo.writeString(operator.getEncodingTable());
        }
        
        System.out.println("Путь к сжатому файлу: " + compressedFile.getAbsolutePath());
        System.out.println("Путь к кодировочной таблице " + table.getAbsolutePath());
        System.out.println("Без таблицы файл будет невозможно извлечь!");
        
        double idealRatio = Math.round(operator.getCompressionRatio() * 100) / (double) 100;//идеализированный коэффициент
        double realRatio = Math.round((double) inputFile.length() 
        		/ ((double) compressedFile.length() + (double) table.length()) * 100) / (double)100;//настоящий коэффициент
        
        System.out.println("Идеализированный коэффициент сжатия равен " + idealRatio);
        System.out.println("Коэффициент сжатия с учетом кодировочной таблицы " + realRatio);
    }

    public static void extract(String filePath, String tablePath) throws FileNotFoundException, IOException {
        HuffmanOperator operator = new HuffmanOperator();
        File compressedFile = new File(filePath),
        	 tableFile = new File(tablePath),
        	 extractedFile = new File(filePath + ".xtr");
        String compressed = "";
        String[] encodingArray = new String[ENCODING_TABLE_SIZE];
        //read compressed file
        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!check here:
        try (FileInputHelper fi = new FileInputHelper(compressedFile)) {
        	byte b;
        	while (true) {
        		b = fi.readByte();//method returns EOFException
        		compressed += String.format("%8s", Integer.toBinaryString(b & 0xff)).replace(" ", "0");
        	}
        } catch (EOFException e) {
        	
        }
        
        //--------------------
        
        //read encoding table:
        try (FileInputHelper fi = new FileInputHelper(tableFile)) {
        	fi.readLine();//skip first empty string
        	encodingArray[(byte)'n'] = fi.readLine();//read code for 'n'
        	while (true) {
        		String s = fi.readLine();
        		if (s == null)
        			throw new EOFException();
        		encodingArray[(byte)s.charAt(0)] = s.substring(1, s.length());        		
        	}
        } catch (EOFException ignore) {}
        
        extractedFile.createNewFile();
        //extract:
		try (FileOutputHelper fo = new FileOutputHelper(extractedFile)) {
			fo.writeString(operator.extract(compressed, encodingArray));
		}
		
		System.out.println("Путь к распакованному файлу " + extractedFile.getAbsolutePath());
    }
}

Kinahanglan nimo nga isulat ang file nga adunay mga panudlo sa readme.txt sa imong kaugalingon 🙂

konklusyon

Tingali mao ra kana ang gusto nakong isulti. Kung adunay ka isulti bahin sa akong kawalay katakus sa mga pag-uswag sa code, algorithm, sa kinatibuk-an, bisan unsang pag-optimize, unya mobati nga gawasnon sa pagsulat. Kung wala koy gipasabut, palihug pagsulat usab. Ganahan ko nga makadungog gikan kanimo sa mga komento!

PS

Oo, oo, ania gihapon ako, tungod kay wala ko kalimti ang bahin sa coefficient. Alang sa string s1, ang encoding table adunay gibug-aton nga 48 bytes - labaw pa sa orihinal nga file, ug wala nila kalimti ang bahin sa mga dugang nga mga sero (ang gidaghanon sa gidugang nga mga zero mao ang 7) => ang compression ratio mahimong ubos sa usa: 176 /(65 + 48*8 + 7) = 0.38. Kung namatikdan usab nimo kini, nan dili lang sa nawong nahuman ka. Oo, kini nga pagpatuman dili kaayo epektibo alang sa gagmay nga mga file. Apan unsa ang mahitabo sa dagkong mga file? Ang mga gidak-on sa file mas dako kay sa gidak-on sa encoding table. Dinhi diin ang algorithm molihok ingon nga kini kinahanglan! Pananglitan, alang sa monologo ni Faust ang archiver naghatag sa usa ka tinuod (dili idealized) coefficient nga katumbas sa 1.46 - hapit usa ug tunga ka beses! Ug oo, ang file naa unta sa English.

Source: www.habr.com

Idugang sa usa ka comment