Datacompressie met behulp van het Huffman-algoritme

Toegang

In dit artikel zal ik het hebben over het beroemde Huffman-algoritme, evenals de toepassing ervan in datacompressie.

Als resultaat zullen we een eenvoudige archiver schrijven. Dit is al besproken artikel over Habré, maar zonder praktische implementatie. Het theoretische materiaal van dit artikel is afkomstig uit de computerwetenschappenlessen op school en uit het boek van Robert Laforet “Data Structures and Algorithms in Java”. Alles is dus afgesneden!

Een beetje reflectie

In een gewoon tekstbestand wordt één teken gecodeerd met 8 bits (ASCII-codering) of 16 (Unicode-codering). Vervolgens zullen we de ASCII-codering bekijken. Neem bijvoorbeeld de regel s1 = “SUSIE SAYS IT IS EASYn”. Er staan ​​uiteraard in totaal 22 tekens in de regel, inclusief spaties en het nieuwe regelteken - 'n'. Het bestand dat deze regel bevat, weegt 22*8 = 176 bits. De vraag rijst meteen: is het rationeel om alle 8 bits te gebruiken om 1 teken te coderen? We gebruiken niet alle ASCII-tekens. Zelfs als dat wel het geval zou zijn, zou het rationeler zijn als de meest voorkomende letter – S – de kortst mogelijke code zou krijgen, en voor de zeldzaamste letter – T (of U, of ‘n’) – om een ​​langere code te krijgen. Dit is waar het Huffman-algoritme uit bestaat: het is noodzakelijk om de optimale coderingsoptie te vinden waarin het bestand het minimale gewicht zal hebben. Het is heel normaal dat de codelengtes voor verschillende symbolen verschillen - dit is waar het algoritme op is gebaseerd.

Codering

Waarom geef je het teken 'S' niet een code van bijvoorbeeld 1 bit lang: 0 of 1. Laat het 1 zijn. Dan is het tweede meest voorkomende teken - ' ' (spatie) - geef 0. Stel je voor dat je bent begonnen met het decoderen van je bericht - de gecodeerde string s1 - en je ziet dat de code begint met 1. Dus wat doe je: is dit het teken S, of is het een ander teken, bijvoorbeeld A? Daarom ontstaat er een belangrijke regel:

Geen van beide codes mag een voorvoegsel zijn van een andere code

Deze regel is de sleutel in het algoritme. Daarom begint het maken van een code met een frequentietabel, die de frequentie (aantal keren) van elk symbool aangeeft:

Datacompressie met behulp van het Huffman-algoritme Tekens die het vaakst voorkomen, moeten het minst worden gecodeerd mogelijk aantal bits. Ik zal een voorbeeld geven van een van de mogelijke codetabellen:

Datacompressie met behulp van het Huffman-algoritme Het gecodeerde bericht ziet er dus als volgt uit:

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

Ik heb de code van elk teken gescheiden door een spatie. Dit zal niet gebeuren in een echt gecomprimeerd bestand!
De vraag rijst: hoe kwam deze jongeman op de code om een ​​tabel met codes te maken? Dit zal hieronder worden besproken.

Een Huffman-boom construeren

Dit is waar binaire zoekbomen te hulp komen. Maak je geen zorgen, je hebt hier de zoek-, invoeg- of verwijdermethoden niet nodig. Hier is de boomstructuur 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;
    }
    ...
}

Dit is niet de volledige code, de volledige code staat hieronder.

Hier is het algoritme voor het construeren van de boom:

  1. Maak een Node-object voor elk teken uit het bericht (regel s1). In ons geval zijn er 9 knooppunten (knooppuntobjecten). Elk knooppunt bestaat uit twee gegevensvelden: symbool en frequentie
  2. Maak voor elk knooppunt een Tree-object (BinaryTree). Het knooppunt wordt de wortel van de boom.
  3. Plaats deze bomen in de prioriteitswachtrij. Hoe lager de frequentie, hoe hoger de prioriteit. Bij het extraheren wordt dus altijd de dervo met de laagste frequentie geselecteerd.

Vervolgens moet u cyclisch het volgende doen:

  1. Verwijder twee bomen uit de prioriteitswachtrij en maak ze kinderen van het nieuwe knooppunt (het nieuw gemaakte knooppunt zonder de letter). De frequentie van het nieuwe knooppunt is gelijk aan de som van de frequenties van de twee afstammelingen.
  2. Maak voor dit knooppunt een boom met de wortel op dit knooppunt. Plaats deze boom terug in de prioriteitswachtrij. (Aangezien de boom een ​​nieuwe frequentie heeft, zal deze hoogstwaarschijnlijk op een nieuwe plaats in de wachtrij verschijnen)
  3. Ga door met stap 1 en 2 totdat er nog maar één boom in de wachtrij staat: de Huffman-boom

Beschouw dit algoritme op regel s1:

Datacompressie met behulp van het Huffman-algoritme

Hier betekent het symbool “lf” (linefeed) een nieuwe regel, “sp” (spatie) is een spatie.

En wat is de volgende stap?

We hebben een Huffman-boom. OK. En wat ermee te doen? Ze nemen het niet eens gratis aan, en dan moet je alle mogelijke paden volgen, van de wortel tot aan de bladeren van de boom. Laten we afspreken dat we een rand 0 aanduiden als deze naar het linkerkind leidt, en 1 als deze naar het rechterkind leidt. Strikt genomen is de code van een symbool in deze notatie het pad van de wortel van de boom naar het blad dat dit symbool bevat.

Datacompressie met behulp van het Huffman-algoritme

Dit is hoe de tabel met codes bleek. Merk op dat als we deze tabel bekijken, we kunnen concluderen over het "gewicht" van elk symbool - dit is de lengte van de code. Vervolgens weegt het originele bestand in gecomprimeerde vorm: 2 * 3 + 2*4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 bits . Aanvankelijk woog het 176 bits. Daarom hebben we het met maar liefst 176/65 = 2.7 keer verminderd! Maar dit is een utopie. Het is onwaarschijnlijk dat een dergelijke coëfficiënt wordt verkregen. Waarom? Dit zal iets later worden besproken.

decoderen

Nou ja, misschien wel het eenvoudigste dat overblijft is decoderen. Ik denk dat velen van jullie al geraden hebben dat we niet zomaar een gecomprimeerd bestand kunnen maken zonder enige hint over hoe het gecodeerd is - we zullen het niet kunnen decoderen! Ja, ja, het was moeilijk voor mij om dit te realiseren, maar ik zal een tekstbestand table.txt moeten maken met een compressietabel:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

Tabelinvoer in de vorm 'symbool' 'tekencode'. Waarom is 01110 zonder symbool? In feite is het met een symbool, het is gewoon dat de Java-tools die ik gebruik bij het uitvoeren naar een bestand, het newline-teken - 'n' - wordt omgezet in een newline (hoe dom het ook mag klinken). Daarom is de lege regel bovenaan het teken voor code 01110. Voor code 00 is het teken een spatie aan het begin van de regel. Ik zal meteen zeggen dat deze methode voor het opslaan van een tabel voor onze Khan-coëfficiënt de meest irrationele kan zijn. Maar het is gemakkelijk te begrijpen en te implementeren. Ik hoor graag uw aanbevelingen in de opmerkingen over optimalisatie.

Met deze tabel is het heel gemakkelijk te decoderen. Laten we onthouden welke regel we hebben gevolgd bij het maken van de codering:

Geen van beide codes mag een voorvoegsel zijn van een andere code

Hier speelt zij een faciliterende rol. We lezen stukje bij beetje opeenvolgend en zodra de resulterende string d, bestaande uit de gelezen bits, overeenkomt met de codering die overeenkomt met het tekenteken, weten we onmiddellijk dat het tekenteken (en alleen dit!) gecodeerd was. Vervolgens schrijven we karakters in de decodeerregel (de regel die het gedecodeerde bericht bevat), stellen we regel d opnieuw in en lezen vervolgens het gecodeerde bestand.

uitvoering

Het is tijd om mijn code te vernederen en een archiverprogramma te schrijven. Laten we het een compressor noemen.

Begin opnieuw. Allereerst schrijven we de Node-klasse:

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

Nu de boom:

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

Prioriteits-rij:

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

De klasse die de Huffman-boom maakt:

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

Klasse die bevat wat codeert/decodeert:

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

Een klasse die het gemakkelijker maakt om naar een bestand te schrijven:

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

Een klasse die het gemakkelijker maakt om uit een bestand te lezen:

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

Nou ja, en de hoofdklasse:

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

Je zult het readme.txt-bestand zelf moeten schrijven :)

Conclusie

Ik denk dat dat alles is wat ik wilde zeggen. Als je iets te zeggen hebt over mijn incompetentie bij het verbeteren van de code, het algoritme of welke optimalisatie dan ook in het algemeen, schrijf dan gerust. Als ik niets heb uitgelegd, schrijf dan ook. Ik hoor graag van je in de reacties!

PS

Ja, ja, ik ben er nog, want ik ben de coëfficiënt niet vergeten. Voor string s1 weegt de coderingstabel 48 bytes - veel groter dan het bronbestand, en we zijn de extra nullen niet vergeten (het aantal toegevoegde nullen is 7) => de compressieverhouding zal minder dan één zijn: 176/ (65 + 48*8 + 7) = 0.38. Als je dit ook hebt opgemerkt, dan is niet alleen je gezicht geweldig. Ja, deze implementatie zal uiterst inefficiënt zijn voor kleine bestanden. Maar wat gebeurt er met grote bestanden? De bestandsgroottes zijn veel groter dan de grootte van de coderingstabel. Dit is waar het algoritme werkt zoals het hoort! Bijvoorbeeld voor Fausts monoloog De archiver produceert een echte (niet geïdealiseerde) coëfficiënt van 1.46 - bijna anderhalf keer! En ja, het bestand moest in het Engels zijn.

Bron: www.habr.com

Voeg een reactie