Kompresi data nggunakake algoritma Huffman

entri

Ing artikel iki aku bakal ngomong babagan algoritma Huffman sing misuwur, uga aplikasi ing kompresi data.

Akibaté, kita bakal nulis arsip prasaja. Iki wis dirembug artikel ing Habré, nanging tanpa implementasine praktis. Materi teoretis saka kiriman saiki dijupuk saka pelajaran ilmu komputer sekolah lan buku Robert Laforet "Struktur Data lan Algoritma ing Jawa". Dadi, kabeh dipotong!

A bayangan sethitik

Ing file teks biasa, siji karakter dienkode nganggo 8 bit (ASCII encoding) utawa 16 (Unicode encoding). Sabanjure kita bakal nimbang encoding ASCII. Contone, njupuk baris s1 = "SUSIE SAYS IT IS EASYn". Ana total 22 karakter ing baris, alamiah, kalebu spasi lan karakter baris anyar - 'n'. Berkas sing ngemot baris iki bobote 22*8 = 176 bit. Pitakonan langsung muncul: apa nyoto nggunakake kabeh 8 bit kanggo encode 1 karakter? Kita ora nggunakake kabeh karakter ASCII. Malah yen padha nindakake, iku bakal luwih nyoto kanggo huruf paling umum - S - diwenehi kode paling cendhak, lan kanggo huruf langka - T (utawa U, utawa 'n') - diwenehi kode maneh. Iki minangka algoritma Huffman: sampeyan kudu nemokake pilihan enkoding sing paling optimal ing ngendi file kasebut bakal duwe bobot minimal. Biasane, dawa kode beda-beda kanggo simbol sing beda - iki adhedhasar algoritma.

Coding

Apa ora menehi karakter 'S' kode, contone, 1 dicokot dawa: 0 utawa 1. Ayo dadi 1. Banjur karakter paling umum kaloro - ' ' (spasi) - menehi 0. Mbayangno sampeyan miwiti dekoding pesen - senar dienkode s1 - lan sampeyan ndeleng sing kode diwiwiti karo 1. Dadi, apa apa: iki karakter S, utawa iku sawetara karakter liyane, contone A? Mulane, ana aturan penting:

Ora ana kode sing kudu dadi awalan liyane

Aturan iki minangka kunci ing algoritma. Mula, nggawe kode diwiwiti kanthi tabel frekuensi, sing nuduhake frekuensi (jumlah kedadeyan) saben simbol:

Kompresi data nggunakake algoritma Huffman Karakter sing paling akeh kedadeyan kudu dikode paling sithik bisa uga jumlah bit. Aku bakal menehi conto salah sawijining tabel kode sing bisa ditindakake:

Kompresi data nggunakake algoritma Huffman Dadi pesen sing dienkode bakal katon kaya iki:

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

Aku misahake kode saben karakter kanthi spasi. Iki ora bakal kedadeyan ing file sing dikompres!
Pitakonan muncul: kepiye carane wong enom iki nggawe kode kanggo nggawe tabel kode? Iki bakal dibahas ing ngisor iki.

Mbangun wit Huffman

Iki ngendi wit telusuran binar teka kanggo ngluwari. Aja kuwatir, sampeyan ora perlu nggoleki, nglebokake, utawa mbusak metode ing kene. Iki minangka struktur wit ing basa Jawa:

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

Iki dudu kode lengkap, kode lengkap bakal ana ing ngisor iki.

Punika algoritma kanggo mbangun wit:

  1. Nggawe obyek Node kanggo saben karakter saka pesen (baris s1). Ing kasus kita bakal ana 9 simpul (objek Node). Saben simpul kasusun saka rong kolom data: simbol lan frekuensi
  2. Nggawe obyek Wit (BinaryTree) kanggo saben Node. Simpul dadi oyod saka wit.
  3. Lebokake wit-witan iki menyang antrian prioritas. Sing luwih murah frekuensi, sing luwih dhuwur prioritas. Mangkono, nalika ngekstrak, dervo kanthi frekuensi paling murah mesthi dipilih.

Sabanjure sampeyan kudu nindakake cyclically ing ngisor iki:

  1. Mbusak wit loro saka antrian prioritas lan nggawe anak saka simpul anyar (simpul sing mentas digawe tanpa huruf). Frekuensi simpul anyar padha karo jumlah frekuensi saka rong wit turunan.
  2. Kanggo simpul iki, gawe wit kanthi oyod ing simpul iki. Lebokake wit iki bali menyang antrian prioritas. (Amarga wit kasebut duwe frekuensi anyar, kemungkinan bakal katon ing papan anyar ing antrian)
  3. Terusake langkah 1 lan 2 nganti mung ana siji wit sing isih ana ing antrian - wit Huffman

Coba algoritma iki ing baris s1:

Kompresi data nggunakake algoritma Huffman

Ing kene simbol "lf" (linefeed) tegese baris anyar, "sp" (spasi) minangka spasi.

Apa sabanjure?

Kita entuk wit Huffman. OK. Lan apa sing kudu dilakoni? Dheweke ora bakal njupuk gratis, banjur sampeyan kudu nglacak kabeh dalan sing bisa ditindakake saka oyod nganti godhong wit. Ayo padha setuju kanggo nunjukake pinggiran 0 yen ndadékaké menyang anak kiwa lan 1 yen ndadékaké menyang sisih tengen. Tegese, ing notasi iki, kode simbol yaiku dalan saka oyod wit nganti godhong sing ngemot simbol iki.

Kompresi data nggunakake algoritma Huffman

Iki minangka tabel kode. Elinga yen kita nimbang Tabel iki, kita bisa nganakke bab "bobot" saben simbol - iki dawa kode sawijining. Banjur, ing wangun kompres, file asli bakal bobot: 2 * 3 + 2 * 4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 bit . Ing wiwitan bobote 176 bit. Akibate, kita suda nganti 176/65 = 2.7 kaping! Nanging iki utopia. Koefisien kaya ngono ora mungkin dipikolehi. Kenging punapa? Iki bakal rembugan sethitik mengko.

Dekoding

Ya, mbok menawa sing paling gampang yaiku decoding. Aku mikir akeh sing ngira yen kita ora bisa nggawe file sing dikompres tanpa ana petunjuk babagan cara dikode - kita ora bakal bisa decode! Ya, ya, angel kanggo aku ngerti iki, nanging aku kudu nggawe file teks table.txt kanthi tabel kompresi:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

Entri tabel ing wangun 'simbol' 'kode karakter'. Napa 01110 tanpa simbol? Ing kasunyatan, iku karo simbol, iku mung alat java aku digunakake nalika outputting menyang file, karakter baris anyar - 'n' - diowahi dadi newline (ora ketompo carane bodho muni). Mulane, baris kosong ing ndhuwur minangka karakter kanggo kode 01110. Kanggo kode 00, karakter minangka spasi ing wiwitan baris. Aku bakal langsung ngomong yen kanggo koefisien Khan kita, cara nyimpen meja iki bisa ngaku paling ora rasional. Nanging gampang dingerteni lan ditindakake. Aku bakal seneng ngrungokake rekomendasi sampeyan ing komentar babagan optimasi.

Duwe tabel iki ndadekake gampang banget kanggo decode. Ayo kita ngelingi aturan apa sing kita tindakake nalika nggawe enkoding:

Ora ana kode sing kudu dadi awalan liyane

Iki ngendi iku muter peran fasilitasi. We maca sequentially dicokot dening dicokot lan, sanalika asil senar d, dumadi saka maca bit, cocog enkoding cocog kanggo karakter karakter, kita langsung ngerti sing karakter karakter (lan mung iku!) iki dienkode. Sabanjure, kita nulis karakter menyang baris dekoding (baris sing ngemot pesen decoded), ngreset baris d, banjur maca file sing dienkode.

Реализация

Wektu kanggo ngremehake kodeku lan nulis arsip. Ayo diarani Kompresor.

Mbaleni. Kaping pisanan, kita nulis kelas 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;
    }
}

Saiki wit:

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

Antrian prioritas:

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

Kelas sing nggawe wit 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;
    }
}

Kelas sing ngemot encode / 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("========================================================");
    }
    }

Kelas sing nggawe luwih gampang nulis menyang 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();
    }
}

Kelas sing luwih gampang diwaca saka 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();
    }
}

Inggih, lan kelas utama:

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

Sampeyan kudu nulis file readme.txt dhewe :)

kesimpulan

Aku kira iku kabeh aku wanted kanggo ngomong. Yen sampeyan duwe apa-apa kanggo ngomong babagan incompetence ing nambah kode, algoritma, utawa optimasi sembarang ing umum, banjur aran bebas nulis. Yen aku durung nerangake apa-apa, tulisen uga. Aku seneng krungu saka sampeyan ing komentar!

PS

Ya wis, aku isih kene, amarga aku ora lali babagan koefisien. Kanggo string s1, tabel enkoding bobote 48 bita - luwih gedhe tinimbang file sumber, lan kita ora lali babagan nul tambahan (jumlah nol sing ditambahake yaiku 7) => rasio kompresi bakal kurang saka siji: 176/ (65 + 48*8 + 7) = 0.38. Yen sampeyan uga ngelingi iki, mula ora mung pasuryan sampeyan sing apik banget. Ya, implementasine iki bakal dadi ora efisien kanggo file cilik. Nanging apa sing kedadeyan karo file gedhe? Ukuran file luwih gedhe tinimbang ukuran tabel enkoding. Iki ngendi algoritma bisa digunakake! Contone, kanggo Monolog Faust Arsip ngasilake koefisien nyata (ora ideal) 1.46 - meh siji setengah kaping! Lan ya, file kasebut mesthine ana ing basa Inggris.

Source: www.habr.com

Add a comment