Cumpressione di dati cù l'algoritmu Huffman

Plus

In questu articulu, parleraghju di u famosu algoritmu di Huffman, è ancu di a so applicazione in a compressione di dati.

In u risultatu, scriveremu un archiviu simplice. Questu hè digià statu articulu nantu à Habré, ma senza implementazione pratica. U materiale teoricu di u postu attuale hè pigliatu da e lezioni di l'informatica di a scola è u libru di Robert Laforet "Data Structures and Algorithms in Java". Allora, tuttu hè sottu u cut!

Un pocu di riflessione

In un schedariu di testu normale, un caratteru hè codificatu cù 8 bit (codificazione ASCII) o 16 (codificazione Unicode). Dopu, cunsideremu a codificazione ASCII. Per esempiu, pigliate a stringa s1 = "SUSIE SAYS IT IS EASYn". In totale, ci sò 22 caratteri in a linea, sicuru, cumpresi i spazii è u caratteru newline - 'n'. U schedariu chì cuntene sta linea pisarà 22 * ​​8 = 176 bits. A quistione hè subitu: hè raziunale di utilizà tutti i 8 bits per codificà 1 caratteru? Ùn avemu micca aduprà tutti i caratteri ASCII. Ancu s'elli eranu, saria più raziunale per dà a lettera più frequente - S - u codice più curtu pussibule, è per a lettera più rara - T (o U, o 'n') - dà u codice più autenticu. Questu hè l'algoritmu Huffman: avete bisognu di truvà a megliu opzione di codificazione, in quale u schedariu serà di pesu minimu. Hè abbastanza normale chì i caratteri diffirenti avarà diverse lunghezze di codice - questu hè a basa di l'algoritmu.

Codificazione

Perchè ùn dà à u caratteru "S" un codice, per esempiu, 1 bit long: 0 o 1. Chì sia 1. Allora u sicondu caratteru più accadutu - " " (spaziu) - daremu 0. Imagine chì avete principiatu à decodificà u vostru missaghju - a stringa codificata s1 - è vede chì u codice cumencia cù 1. Allora, chì fà : hè un altru caratteru S, o cum'è un altru caratteru ? Per quessa, hè una regula impurtante:

Nisun codice deve esse un prefissu di un altru

Sta regula hè a chjave per l'algoritmu. Per quessa, a creazione di u codice principia cù una tabella di freccia, chì indica a freccia (numeru d'occurrence) di ogni simbulu:

Cumpressione di dati cù l'algoritmu Huffman I caratteri cù a maiò parte di l'occurrenze deve esse codificati cù u menu menu pussibule u numeru di bits. Daraghju un esempiu di una di e pussibuli tabelle di codice:

Cumpressione di dati cù l'algoritmu Huffman Allora u missaghju codificatu sarà cusì:

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

Aghju separatu u codice di ogni caratteru cù un spaziu. Questu ùn succede micca veramente in un schedariu cumpressu!
A quistione nasce: cumu hà fattu questu rookie cun un codice cumu per creà una tabella di codice? Questu serà discutitu quì sottu.

Custruì un Arburu Huffman

Questu hè induve l'arbureti di ricerca binari venenu in salvezza. Ùn vi preoccupate, ùn avete micca bisognu di i metudi di ricerca, inserimentu è sguassate quì. Eccu a struttura di l'arburu in 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;
    }
    ...
}

Questu ùn hè micca u codice cumpletu, u codice sanu serà quì sottu.

Eccu l'algoritmu per custruisce un arbre:

  1. Crea un oggettu Node per ogni caratteru da u messagiu (linea s1). In u nostru casu, ci saranu 9 nodi (oggetti Node). Ogni node hè custituitu da dui campi di dati: simbulu è frequenza
  2. Crea un oggettu Tree (BinaryTree) per ognunu di i nodi Node. U node diventa a radica di l'arbulu.
  3. Inserite questi arburi in a fila di priorità. A più bassa a frequenza, più alta a priorità. Cusì, quandu l'estrazione, l'arbulu cù a freccia più bassa hè sempre sceltu.

Dopu, avete bisognu di fà ciclicamente i seguenti:

  1. Recuperate dui arburi da a fila di priorità è facenu i figlioli di un novu node (un nodu novu creatu senza lettera). A freccia di u novu node hè uguali à a summa di e frequenze di i dui arburi discendenti.
  2. Per questu node, crea un arbre arradicatu à questu node. Inserite stu arbre torna in a fila di priorità. (Siccomu l'arbulu hà una nova frequenza, u più prubabilmente entre in un novu postu in a fila)
  3. Cuntinuà i passi 1 è 2 finu à chì un arbre hè lasciatu in a fila - l'arburu Huffman

Cunsiderate stu algoritmu nantu à a linea s1:

Cumpressione di dati cù l'algoritmu Huffman

Quì u simbulu "lf" (linefeed) denota una nova linea, "sp" (spaziu) hè un spaziu.

E qual'ede avà?

Avemu l'arburu Huffman. OK. È chì fà cun ellu? Ùn pigliaranu micca per liberu, è dopu, avete bisognu di traccia tutti i camini pussibuli da a radica à e foglie di l'arbulu. Simu d'accordu per etichettate un bordu 0 s'ellu porta à u zitellu manca è 1 s'ellu porta à u dirittu. In modu strettu, in queste notazioni, u codice di caratteri hè u percorsu da a radica di l'arbulu à a foglia chì cuntene stu caratteru.

Cumpressione di dati cù l'algoritmu Huffman

Cusì, a tavula di codici turnò fora. Innota chì si cunsiderà sta tabella, pudemu cuncludi nantu à u "pesu" di ogni caratteru - questu hè a durata di u so codice. Allora, in forma cumpressa, u schedariu fonte piserà: 2 * 3 + 2 * 4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 bits. À u principiu, pisava 176 bits. Dunque, l'avemu ridutta quant'è 176/65 = 2.7 volte! Ma questu hè un utopia. Un tali rapportu hè improbabile di ottene. Perchè? Questu serà discututu un pocu dopu.

Decodificazione

Ebbè, forse a cosa più simplice chì resta hè a decodificazione. Pensu chì parechji di voi avete indovinatu chì hè impussibile di creà simpliciamente un schedariu cumpressatu senza alcunu suggerimentu nantu à cumu hè statu codificatu - ùn pudemu micca decodificà! Iè, iè, era difficiule per mè per capisce questu, ma aghju da creà un schedariu di testu table.txt cù una tabella di compressione:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

Ingressu di a tabella in a forma "carattere" "codice di caratteru". Perchè 01110 hè senza un simbulu? In fatti, hè cun un simbulu, solu l'arnesi java chì aghju utilizatu quandu u output à un schedariu, u caratteru di novu linea - 'n' - hè cunvertitu in una nova linea (ùn importa quantu stupidu sona). Dunque, a linea viota sopra hè u caratteru per u codice 01110. Per u codice 00, u caratteru hè un spaziu à u principiu di a linea. Devu dì subitu chì stu metudu di almacenà a tavula pò esse u più irrazionale per u nostru coefficient khan. Ma hè faciule da capisce è implementà. Seraghju felice di sente i vostri cunsiglii in i cumenti nantu à l'ottimisazione.

Cù sta tavula, hè assai faciule di decode. Ricurdemu da quale regula eramu guidati quandu creamu a codificazione:

Nisun codice deve esse un prefissu di un altru

Hè quì chì ghjoca un rolu facilitatore. Leghjemu pocu à pocu in sequenza, è appena a stringa resultanti d, custituita da i bits letti, currisponde à a codificazione chì currisponde à u caratteru di u caratteru, sapemu subitu chì u caratteru di u caratteru (è solu !) hè statu codificatu. In seguitu, scrivemu carattere à a stringa di decode (a stringa chì cuntene u messagiu decoded), resettate a stringa d, è leghje u schedariu codificatu più.

Реализация

Hè ora di umilià u mo codice scrivendu un archiver. Chjamemu Compressor.

Cumincià da novu. Prima di tuttu, scrivemu a classa 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;
    }
}

Avà l'arburu:

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

Coda di priorità:

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

A classa chì crea l'arburu 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;
    }
}

A classa chì cuntene quale codifica / decodifica:

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

Una classa chì facilita a scrittura à un schedariu:

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

Una classa chì facilita a lettura da un schedariu:

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

Ebbè, è a classa principale:

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

Avete da scrive u schedariu cù istruzzioni readme.txt voi stessu 🙂

cunchiusioni

Pensu chì hè tuttu ciò chì vulia dì. Sè vo avete qualcosa à dì nantu à a mo incompetenza di megliurenze in u codice, algoritmu, in generale, ogni ottimisazione, allora sentu liberu di scrive. Se ùn aghju micca spiegatu qualcosa, scrivite ancu. Mi piacerebbe à sente da voi in i cumenti!

PS

Iè, sì, sò sempre quì, perchè ùn aghju micca scurdatu di u coefficient. Per a stringa s1, a tavola di codificazione pesa 48 bytes - assai più di u schedariu originale, è ùn anu micca scurdatu di i zeri extra (u numaru di zeri aghjuntu hè 7) => u rapportu di compressione serà menu di unu: 176/(65 + 48 * 8 + 7) = 0.38. Sè vo ancu nutari stu, allura ghjustu micca in faccia vi sò fattu. Iè, sta implementazione serà estremamente inefficace per i schedarii chjuchi. Ma chì succede à i schedarii grossi? E dimensioni di u schedariu sò assai più grande di a dimensione di a tavola di codificazione. Questu hè induve l'algoritmu funziona cum'è deve! Per esempiu, per U monologu di Faust l'archiver dà un coefficient reale (micca idealizatu) uguale à 1.46 - quasi una volta è mezu! È iè, u schedariu duvia esse in inglese.

Source: www.habr.com

Add a comment