Isku-buufinta xogta iyadoo la adeegsanayo algorithm-ka Huffman

entry

Maqaalkan waxaan ka hadli doonaa algorithm-ka caanka ah ee Huffman, iyo sidoo kale codsigeeda xogta xogta.

Natiijo ahaan, waxaan qori doonaa kayd fudud. Tan hore ayaa looga hadlay maqaal ku saabsan Habré, laakiin aan la fulin wax ku ool ah. Maaddada aragtida ee boostada hadda waxaa laga soo qaatay casharrada sayniska kombiyuutarka ee dugsiga iyo buugga Robert Laforet ee "Habka Xogta iyo Algorithms ee Java". Markaa, wax walba waa la gooyay!

Dhowr fikradood

Faylka qoraalka caadiga ah, hal xaraf ayaa lagu dhejiyay 8 bits (ASCII encoding) ama 16 (Unicode encoding). Marka xigta waxaan tixgelin doonaa ASCII codeing. Tusaale ahaan, soo qaado xariiqda s1 = "SUSIE waxay tidhaahdaa way fududahay". Waxaa jira wadar ahaan 22 xaraf oo khadka ku jira, si dabiici ah, oo ay ku jiraan meelo bannaan iyo dabeecadda xariiqda cusub - 'n'. Faylka ka kooban xariiqan wuxuu miisaankiisu noqon doonaa 22*8 = 176 bits. Su'aashu waxay soo baxdaa isla markiiba: ma caqli galbaa in la isticmaalo dhammaan 8-bit si loo codeeyo 1 xaraf? Ma isticmaalno dhammaan xarfaha ASCII Xitaa haddii ay yeeleen, waxay noqon doontaa mid caqli gal ah in xarafka ugu caansan - S - la siiyo koodka ugu gaaban ee suurtogalka ah, iyo xarafka dhifka ah - T (ama U, ama 'n') - in la siiyo kood dheer. Tani waa waxa Huffman algorithm ka kooban yahay: waa lagama maarmaan in la helo ikhtiyaarka codaynta ugu fiican kaas oo feylku yeelan doono miisaanka ugu yar. Waa wax iska caadi ah in dhererka koodku ku kala duwanaan doono calaamado kala duwan - tani waa waxa algorithm ku salaysan.

Codaynta

Maxaad u siin weyday jilaa 'S' kood, tusaale ahaan, 1 xoogaa dheer: 0 ama 1. Ha ahaato 1. Ka dib dabeecadda labaad ee ugu caansan - '' (space) - sii 0. Bal qiyaas inaad bilawday decoding fariintaada - s1 xarriiqda ku lifaaqan - oo waxaad arkaysaa in koodku ka bilaabmayo 1. Haddaba, maxaad samaynaysaa: kani ma jilaagii S, mise waa dabeecad kale, tusaale ahaan A? Sidaa darteed, xeer muhiim ah ayaa soo baxaya:

Midkoodna kood waa inuusan horgale u noqon mid kale

Xeerkani waa furaha algorithmamka. Sidaa darteed, abuurista kood waxay ku bilaabataa miis joogto ah, kaasoo tilmaamaya inta jeer ee (tirada dhacdooyinka) calaamad kasta:

Isku-buufinta xogta iyadoo la adeegsanayo algorithm-ka Huffman Calaamadaha dhacdooyinka ugu badan waa in lagu dhejiyaa ugu yaraan suurtagal ah tirada jajabyada. Waxaan ku siin doonaa tusaale mid ka mid ah jaantusyada kood ee suurtogalka ah:

Isku-buufinta xogta iyadoo la adeegsanayo algorithm-ka Huffman Haddaba farriinta la suray waxay u ekaan doontaa sidan:

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

Waxaan kala saaray koodka xaraf kasta oo meel bannaan leh. Tani kuma dhici doonto fayl dhab ah oo la cufay!
Su'aashu waxay soo baxaysaa: sidee buu wiilkan yar ula yimid koodhka si uu u sameeyo shaxda codes? Tan ayaa hoos looga hadli doonaa.

Dhisidda geedka Huffman

Tani waa meesha geedaha raadinta binary ay u soo gurmadaan. Ha werwerin, uma baahnid raadinta, gelida, ama tirtirida hababka halkan. Waa kan qaabka geedka 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;
    }
    ...
}

Kani maaha koodka dhamaystiran, koodka buuxa ayaa hoos ahaan doona.

Waa kan algorithm-ka lagu dhisayo geedka:

  1. U samee shayga Node xaraf kasta fariinta (line s1). Xaaladeena waxaa jiri doona 9 nodes ( Walxaha Node). Node kastaa wuxuu ka kooban yahay laba qaybood oo xog ah: calaamad iyo inta jeer
  2. U samee Shayga Geedka (BinaryTree) Node kasta. Noodku wuxuu noqdaa xididka geedka.
  3. Geli geedahaan safka mudnaanta leh. Inta jeer ee hooseeya, ayaa ah mudnaanta sare. Markaa, marka la soo saarayo, dervo leh inta jeer ee ugu hooseeya ayaa had iyo jeer la doortaa.

Marka xigta waxaad u baahan tahay inaad sameyso si wareeg ah:

  1. Ka saar laba geed oo safka mudnaanta leh oo ka dhig carruur ka mid ah noodhka cusub (node ​​cusub oo la sameeyay oo aan xaraf lahayn). Inta jeer ee qanjirada cusubi waxay la mid tahay wadarta soo noqnoqoshada labada geed ee farcanka ah.
  2. Noodkan, u samee geed xididku ku leeyahay noodhkan. Geli geedkan safka mudnaanta leh. (Maadaama geedku leeyahay soo noqnoqoshada cusub, waxay u badan tahay inay ka soo muuqan doonto meel cusub oo safka ah)
  3. Sii wad tillaabooyinka 1 iyo 2 ilaa inta uu jiro hal geed oo keliya ayaa ka hadhay safka - geedka Huffman

Tixgeli algorithm-kan khadka s1:

Isku-buufinta xogta iyadoo la adeegsanayo algorithm-ka Huffman

Halkan calaamadda "lf" (linefeed) macnaheedu waa xariiq cusub, "sp" (space) waa meel bannaan.

Maxaa xiga?

Waxaan helnay geed Huffman ah. OK Maxaase lagu sameeyaa? Xitaa bilaash kuma qaadan doonaan, ka dibna, waxaad u baahan tahay inaad raadiso dhammaan waddooyinka suurtagalka ah ee xididka ilaa caleemaha geedka. Aynu ku heshiino inaan muujino cidhifka 0 haddii ay u hogaamiso ilmaha bidix iyo 1 haddii ay u hoggaamiso midka midig. Marka si adag loo hadlo, tilmaantan, koodka astaantu waa dariiqa xididka geedka ilaa caleenta ay ku jirto astaantan.

Isku-buufinta xogta iyadoo la adeegsanayo algorithm-ka Huffman

Sidan ayaa shaxda code-ku u noqday. Ogsoonow in haddii aan tixgelinno shaxdan, waxaan ku soo gabagabeyn karnaa "miisaanka" calaamad kasta - tani waa dhererka koodka. Kadib, qaab la cufan, faylka asalka ahi wuxuu miisaami doonaa: 2 * 3 + 2*4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 bits . Markii hore waxa uu miisaankiisu ahaa 176 bits. Sidaas awgeed, waxaanu hoos u dhignay ilaa 176/65 = 2.7 jeer! Laakiin tani waa utopia. Isku xidhka noocan oo kale ah uma badna in la helo. Waa maxay sababtu? Tan ayaa laga hadli doonaa wax yar ka dib.

Dejinta

Hagaag, malaha waxa ugu fudud ee hadhay waa dejinta Waxaan u maleynayaa in qaar badan oo idinka mid ah ay qiyaaseen in aanan si fudud u abuuri karin fayl la isku dhejiyay iyada oo aan wax tilmaam ah laga helin sida loo codeeyay - ma awoodi doono inaan go'aamino! Haa, haa, way igu adkeyd inaan tan garto, laakiin waa inaan abuuraa faylka qoraalka table.txt oo leh miis cadaadis ah:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

Gelida shaxda ee foomka 'calaamadaha' 'code character'. Waa maxay sababta 01110 aan calaamad lahayn? Dhab ahaantii, waxay la socotaa calaamad, waa uun in qalabka Java ee aan isticmaalo marka la soo saarayo faylka, dabeecadda khadka cusub - 'n' - loo beddelo khad cusub (iyadoon loo eegin sida nacasnimada ay u dhawaaqi karto). Sidaa darteed, xariiqda madhan ee sare waa calaamadda koodka 01110. Koodhka 00, jilaagu waa meel bannaan oo ku taal bilowga laynka. Isla markiiba waxaan dhihi doonaa isku-dhafka Khan-ka, habkan lagu keydinayo miiska wuxuu sheegan karaa inuu yahay midka ugu caqliga badan. Laakiin way fududahay in la fahmo oo la hirgeliyo. Waxaan ku farxi doonaa inaan maqlo talooyinkaaga faallooyinka ku saabsan hagaajinta.

Lahaanshaha miiskan ayaa aad u fududaynaysa in la kala saaro. Aynu xasuusanno qaanuunka aanu raacnay markii aanu samaynaynay codaynta:

Midkoodna kood waa inuusan horgale u noqon mid kale

Tani waa meesha ay ka ciyaareyso doorka fududeynta. Waxaan si isdaba joog ah u akhrinnay xoogaa xoogaa, isla markii xarriiqa ka soo baxay d, ee ka kooban qaybinta akhriska, uu ku habboon yahay codaynta u dhiganta jilaha, waxaan isla markiiba ogaannay in jilaaga (oo kaliya!) la duubay. Marka xigta, waxaan ku qornaa xarfaha xariiqda codeeynta (xariiqda ay ku jirto fariinta la go'aamiyay), dib u dajinta d, ka dibna akhri faylka la calaamadeeyay.

Реализация

Waa waqtigii aan ceebayn lahaa koodkayga oo aan qori lahaa kayd. Aan ugu yeerno Compressor.

Dib u bilow Marka hore, waxaanu qoraynaa fasalka 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;
    }
}

Hadda geedka:

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

Safka mudnaanta leh:

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

Fasalka abuura geedka 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;
    }
}

Fasalka uu ku jiro kaas oo codeeynaya/dejiya:

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

Fasalka fududeeya in faylka loo qoro:

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

Fasalka fududeeya in faylka laga akhriyo:

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

Hagaag, iyo fasalka ugu weyn:

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

Waa inaad adigu qortaa faylka readme.txt :)

gunaanad

Waxaan filayaa inay taasi tahay waxa aan rabay inaan sheego. Haddii aad hayso wax aad ka tidhaahdo karti-darradayda xagga hagaajinta koodhka, algorithm, ama wax ka beddelka guud ahaan, ka dib waxaad xor u tahay inaad wax qorto. Haddii aanan waxba sharaxin, fadlan sidoo kale qor. Waxaan jeclaan lahaa inaan kaa maqlo faallooyinka!

PS

Haa, haa, weli halkan ayaan joogaa, sababtoo ah maan iloobin isku-dhafka. Xadhiga s1, miiska codaynta ayaa miisaankiisu yahay 48 bytes - aad ayuu uga weyn yahay faylka isha, mana aan iloobin eberyada dheeraadka ah (tirada eber ee lagu daray waa 7) => saamiga isku-buufintu wuxuu noqon doonaa wax ka yar hal: 176/ (65 + 48*8 + 7) = 0.38. Haddii aad sidoo kale dareentay tan, markaa maaha kaliya wajigaaga mid aad u fiican. Haa, hirgalintani waxay noqon doontaa mid aan waxtar u lahayn faylasha yaryar. Laakiin maxaa ku dhacaya faylasha waaweyn? Cabbirrada faylka ayaa aad uga weyn cabbirka miiska cod-bixinta. Tani waa meesha algorithm u shaqeeyo sidii la rabay! Tusaale ahaan, loogu talagalay Hal-ku-dhegga Faust Kaydka ayaa soo saara iskudar dhab ah (aan ahayn mid ku habboon) oo ah 1.46 - ku dhawaad ​​hal jeer iyo badh! Haa, feylku wuxuu ku qornaa Ingiriis.

Source: www.habr.com

Add a comment