Kompressjoni tad-dejta bl-użu tal-algoritmu Huffman

Dħul

F'dan l-artikolu ser nitkellem dwar l-algoritmu famuż Huffman, kif ukoll l-applikazzjoni tiegħu fil-kompressjoni tad-dejta.

Bħala riżultat, aħna se niktbu arkivju sempliċi. Dan diġà ġie diskuss artiklu dwar Habré, iżda mingħajr implimentazzjoni prattika. Il-materjal teoretiku tal-kariga attwali huwa meħud mill-lezzjonijiet tax-xjenza tal-kompjuter tal-iskola u l-ktieb ta 'Robert Laforet "Data Structures and Algorithms in Java". Allura, kollox huwa maqtugħ!

Ftit ħsibijiet

F'fajl ta 'test regolari, karattru wieħed huwa kodifikat bi 8 bits (kodifikazzjoni ASCII) jew 16 (kodifikazzjoni Unicode). Sussegwentement se nikkunsidraw il-kodifikazzjoni ASCII. Pereżempju, ħu l-linja s1 = "SUSIE TGĦID LI IS EASYn". Hemm total ta' 22 karattru fil-linja, naturalment, inklużi l-ispazji u l-karattru tal-linja l-ġdida - 'n'. Il-fajl li jkun fih din il-linja se jiżen 22 * ​​8 = 176 bit. Il-mistoqsija tqum immedjatament: huwa razzjonali li tuża t-8 bits kollha biex tikkodifika karattru 1? Aħna ma nużawx il-karattri ASCII kollha. Anke kieku għamlu, ikun aktar razzjonali li l-aktar ittra komuni - S - tingħata l-iqsar kodiċi possibbli, u li l-aktar ittra rari - T (jew U, jew 'n') - tingħata kodiċi itwal. Dan huwa dak li jikkonsisti l-algoritmu Huffman: huwa meħtieġ li tinstab l-aħjar għażla ta 'kodifikazzjoni li fiha l-fajl ikollu l-piż minimu. Huwa pjuttost normali li t-tulijiet tal-kodiċi jvarjaw għal simboli differenti - fuq dan huwa bbażat l-algoritmu.

Kodifikazzjoni

Għaliex ma tagħtix il-karattru 'S' kodiċi, pereżempju, 1 bit twil: 0 jew 1. Ħalliha tkun 1. Imbagħad it-tieni karattru l-aktar komuni - ' ' (spazju) - agħti 0. Immaġina li bdejt jiddekodifika l-messaġġ tiegħek - is-sekwenza kodifikata s1 - u tara li l-kodiċi jibda b'1. Allura, x'tagħmel: dan huwa l-karattru S, jew xi karattru ieħor, pereżempju A? Għalhekk, tqum regola importanti:

L-ebda kodiċi m'għandu jkun prefiss ta' ieħor

Din ir-regola hija essenzjali fl-algoritmu. Għalhekk, il-ħolqien ta 'kodiċi jibda b'tabella tal-frekwenza, li tindika l-frekwenza (numru ta' okkorrenzi) ta 'kull simbolu:

Kompressjoni tad-dejta bl-użu tal-algoritmu Huffman Karattri bl-aktar okkorrenzi għandhom ikunu kodifikati l-inqas possibbli numru ta' bits. Se nagħti eżempju ta 'waħda mit-tabelli tal-kodiċi possibbli:

Kompressjoni tad-dejta bl-użu tal-algoritmu Huffman Allura l-messaġġ kodifikat se jidher bħal dan:

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

I separati l-kodiċi ta 'kull karattru bi spazju. Dan mhux se jiġri f'fajl tassew kompressat!
Tqum il-mistoqsija: kif dan iż-żagħżugħ ħareġ bil-kodiċi biex joħloq tabella ta 'kodiċi? Dan se jiġi diskuss hawn taħt.

Bini ta 'siġra Huffman

Dan huwa fejn is-siġar tat-tiftix binarji jiġu għas-salvataġġ. Tinkwetax, mhux se jkollok bżonn il-metodi ta' tfittxija, daħħal jew ħassar hawn. Hawnhekk hawn l-istruttura tas-siġra f'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;
    }
    ...
}

Dan mhuwiex il-kodiċi komplut, il-kodiċi sħiħ se jkun hawn taħt.

Hawn hu l-algoritmu għall-kostruzzjoni tas-siġra:

  1. Oħloq oġġett Node għal kull karattru mill-messaġġ (linja s1). Fil-każ tagħna se jkun hemm 9 nodi (Oġġetti Node). Kull nodu jikkonsisti f'żewġ oqsma tad-dejta: simbolu u frekwenza
  2. Oħloq oġġett Tree (BinaryTree) għal kull Node. In-nodu jsir l-għerq tas-siġra.
  3. Daħħal dawn is-siġar fil-kju prijoritarju. Aktar ma tkun baxxa l-frekwenza, iktar tkun għolja l-prijorità. Għalhekk, meta jiġi estratt, dejjem jintgħażel id-dervo bl-inqas frekwenza.

Imbagħad trid tagħmel dan li ġej b'mod ċikliku:

  1. Neħħi żewġ siġar mill-kju prijoritarju u tagħmilhom tfal tan-nodu l-ġdid (in-nodu maħluq ġdid mingħajr l-ittra). Il-frekwenza tan-nodu l-ġdid hija ugwali għas-somma tal-frekwenzi taż-żewġ siġar dixxendenti.
  2. Għal dan in-node, oħloq siġra bl-għerq f'dan in-node. Daħħal din is-siġra lura fil-kju prijoritarju. (Peress li s-siġra għandha frekwenza ġdida, x'aktarx tidher f'post ġdid fil-kju)
  3. Kompli l-passi 1 u 2 sakemm fadal siġra waħda biss fil-kju - is-siġra Huffman

Ikkunsidra dan l-algoritmu fuq il-linja s1:

Kompressjoni tad-dejta bl-użu tal-algoritmu Huffman

Hawnhekk is-simbolu "lf" (linefeed) ifisser linja ġdida, "sp" (spazju) huwa spazju.

U dak li jmiss?

Aħna ltqajna siġra Huffman. KOLLOX SEW. U x'għandek tagħmel biha? Huma lanqas biss se jeħduha b'xejn.U mbagħad, għandek bżonn traċċa l-mogħdijiet kollha possibbli mill-għerq sal-weraq tas-siġra. Ejja naqblu li nindikaw tarf 0 jekk iwassal għat-tifel tax-xellug u 1 jekk iwassal għal dak tal-lemin. Strettament, f'din in-notazzjoni, il-kodiċi ta 'simbolu huwa t-triq mill-għerq tas-siġra sal-werqa li fiha dan is-simbolu stess.

Kompressjoni tad-dejta bl-użu tal-algoritmu Huffman

Hekk irriżultaw it-tabella tal-kodiċi. Innota li jekk nikkunsidraw din it-tabella, nistgħu nikkonkludu dwar il-"piż" ta 'kull simbolu - dan huwa t-tul tal-kodiċi tiegħu. Imbagħad, f'forma kkompressata, il-fajl oriġinali jiżen: 2 * 3 + 2*4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 bits . Għall-ewwel kien jiżen 176 bit. Konsegwentement, naqqasniha b'176/65 = 2.7 darbiet! Iżda din hija utopja. Tali koeffiċjent mhux probabbli li jinkiseb. Għaliex? Dan se jiġi diskuss ftit aktar tard.

Dekodifikazzjoni

Ukoll, forsi l-aktar ħaġa sempliċi li fadal hija d-dekodifikazzjoni. Naħseb li ħafna minnkom ħasbu li ma nistgħux sempliċement noħolqu fajl kompressat mingħajr ebda ħjiel ta 'kif ġie kodifikat - mhux se nkunu nistgħu niddekodifikawh! Iva, iva, kien diffiċli għalija li nirrealizza dan, imma se jkolli noħloq fajl ta 'test table.txt b'tabella ta' kompressjoni:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

Entrata fit-tabella fil-forma 'simbolu' 'kodiċi tal-karattru'. Għaliex 01110 huwa mingħajr simbolu? Fil-fatt, huwa b'simbolu, huwa biss li l-għodda java li nuża meta noħroġ għal fajl, il-karattru newline - 'n' - huwa kkonvertit f'newline (irrispettivament minn kemm jista 'ħoss stupid). Għalhekk, il-linja vojta fil-quċċata hija l-karattru għall-kodiċi 01110. Għall-kodiċi 00, il-karattru huwa spazju fil-bidu tal-linja. Jien ngħid mill-ewwel li għall-koeffiċjent Khan tagħna, dan il-metodu ta 'ħażna ta' tabella jista 'jistqarr li huwa l-aktar irrazzjonali. Iżda huwa faċli li tifhem u timplimenta. Inkun kuntent li nisma' r-rakkomandazzjonijiet tiegħek fil-kummenti dwar l-ottimizzazzjoni.

Li jkollok din it-tabella tagħmilha faċli ħafna biex tiġi dekodifikata. Ejja niftakru liema regola segwejna meta ħloqna l-kodifikazzjoni:

L-ebda kodiċi m'għandu jkun prefiss ta' ieħor

Dan huwa fejn għandu rwol ta’ faċilitazzjoni. Naqraw sekwenzjali bit bit u, hekk kif is-sekwenza li tirriżulta d, li tikkonsisti mill-bits moqrija, taqbel mal-kodifikazzjoni li tikkorrispondi għall-karattru tal-karattru, nafu immedjatament li l-karattru tal-karattru (u biss!) ġie kodifikat. Sussegwentement, niktbu karattru fil-linja ta 'dekodifikazzjoni (il-linja li fiha l-messaġġ dekodifikat), reset linja d, u mbagħad aqra l-fajl kodifikat.

Реализация

Wasal iż-żmien li numilja l-kodiċi tiegħi u nikteb arkivjar. Ejja nsejħulha Kompressur.

Ibda mill-bidu. L-ewwelnett, niktbu l-klassi 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;
    }
}

Issa s-siġra:

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;
    }
}

Kju ta' Prijorità:

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;//возвращаем удаленный элемент(элемент с наименьшей частотой)
    }
}

Il-klassi li toħloq is-siġra 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;
    }
}

Klassi li fiha liema tikkodifika/dekodifika:

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("========================================================");
    }
    }

Klassi li tagħmilha aktar faċli biex tikteb f'fajl:

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();
    }
}

Klassi li tagħmilha aktar faċli biex taqra minn fajl:

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();
    }
}

Ukoll, u l-klassi ewlenija:

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());
    }
}

Ikollok tikteb il-fajl readme.txt lilek innifsek :)

Konklużjoni

Naħseb li dak kollu li ridt ngħid. Jekk għandek xi tgħid dwar l-inkompetenza tiegħi fit-titjib tal-kodiċi, l-algoritmu, jew kwalunkwe ottimizzazzjoni b'mod ġenerali, imbagħad tħossok liberu li tikteb. Jekk ma spjegajt xejn, jekk jogħġbok ikteb ukoll. Nixtieq nisma' mingħandkom fil-kummenti!

PS

Iva, iva, għadni hawn, għax ma nsejtx il-koeffiċjent. Għal string s1, it-tabella tal-kodifikazzjoni tiżen 48 bytes - ħafna akbar mill-fajl tas-sors, u ma ninsewx dwar iż-żeri addizzjonali (in-numru ta 'żeri miżjuda huwa 7) => il-proporzjon tal-kompressjoni se jkun inqas minn wieħed: 176/ (65 + 48*8 + 7) = 0.38. Jekk innotajt ukoll dan, allura mhux wiċċek biss hu kbir. Iva, din l-implimentazzjoni se tkun estremament ineffiċjenti għal fajls żgħar. Imma x'jiġri minn fajls kbar? Id-daqsijiet tal-fajl huma ħafna akbar mid-daqs tat-tabella tal-kodifikazzjoni. Dan huwa fejn l-algoritmu jaħdem kif suppost! Per eżempju, għal Il-monologu ta' Faust L-arkivju jipproduċi koeffiċjent reali (mhux idealizzat) ta '1.46 - kważi darba u nofs! U iva, il-fajl suppost kellu jkun bl-Ingliż.

Sors: www.habr.com

Żid kumment