Gagnaþjöppun með Huffman reikniritinu

Færslu

Í þessari grein mun ég tala um hið vel þekkta Huffman reiknirit, sem og notkun þess í gagnaþjöppun.

Fyrir vikið munum við skrifa einfaldan skjalavörð. Þetta hefur þegar verið grein um Habré, en án verklegrar framkvæmdar. Fræðilegt efni núverandi færslu er tekið úr tölvunarfræðikennslu skóla og bók Robert Laforet "Data Structures and Algorithms in Java". Svo, allt er undir högg að sækja!

Smá hugleiðing

Í venjulegri textaskrá er einn stafur kóðaður með 8 bitum (ASCII kóðun) eða 16 (Unicode kóðun). Næst munum við íhuga ASCII kóðunina. Til dæmis, taktu strenginn s1 = "SUSIE SEGIR ÞAÐ ER Auðvelt". Alls eru 22 stafir í línunni, að sjálfsögðu, að meðtöldum bilum og nýlínustafnum - 'n'. Skráin sem inniheldur þessa línu mun vega 22*8 = 176 bita. Spurningin vaknar strax: er skynsamlegt að nota alla 8 bitana til að kóða 1 staf? Við notum ekki alla ASCII stafi. Jafnvel þótt þeir væru það, væri skynsamlegra að gefa algengasta stafinn - S - stysta mögulega kóðann, og fyrir sjaldgæfasta stafinn - T (eða U, eða 'n') - gefa kóðann ekta. Þetta er Huffman reikniritið: þú þarft að finna besta kóðunvalkostinn, þar sem skráin verður af lágmarksþyngd. Það er alveg eðlilegt að mismunandi stafir hafi mismunandi kóðalengd - þetta er grunnurinn að reikniritinu.

Kóðun

Af hverju ekki að gefa stafnum 'S' kóða, til dæmis, 1 bita langan: 0 eða 1. Látum það vera 1. Síðan er sá stafur sem kemur næst mest fyrir - ' ' (bil) - við gefum 0. Ímyndaðu þér, þú byrjaðir að afkóða skilaboðin þín - kóðaður strengur s1 - og þú sérð að kóðinn byrjar á 1. Svo, hvað á að gera: er það stafurinn S, eða er það einhver annar stafur, eins og A? Þess vegna kemur mikilvæg regla:

Enginn kóði má vera forskeyti annars

Þessi regla er lykillinn að reikniritinu. Þess vegna byrjar stofnun kóðans með tíðnitöflu, sem gefur til kynna tíðni (fjöldi tilvika) hvers tákns:

Gagnaþjöppun með Huffman reikniritinu Stafirnir með flestar tilvik ættu að vera kóðaðar með fæstum mögulegt fjölda bita. Ég mun gefa dæmi um eina af mögulegum kóðatöflum:

Gagnaþjöppun með Huffman reikniritinu Svo kóðuðu skilaboðin munu líta svona út:

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

Ég aðskildi kóða hvers stafs með bili. Þetta mun í raun ekki gerast í þjappaðri skrá!
Spurningin vaknar: hvernig kom þessi nýliði upp með kóða hvernig á að búa til kóðatöflu? Um þetta verður fjallað hér á eftir.

Að byggja Huffman tré

Þetta er þar sem tvíleitartré koma til bjargar. Ekki hafa áhyggjur, þú þarft ekki að leita, setja inn og eyða aðferðunum hér. Hér er trébyggingin í 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;
    }
    ...
}

Þetta er ekki heill kóðinn, allur kóðinn verður að neðan.

Hér er reikniritið til að byggja tré:

  1. Búðu til Node hlut fyrir hvern staf úr skilaboðunum (lína s1). Í okkar tilviki verða 9 hnútar (Node objects). Hver hnút samanstendur af tveimur gagnasviðum: tákni og tíðni
  2. Búðu til Tree hlut (BinaryTree) fyrir hvern hnúta. Hnúturinn verður rót trésins.
  3. Settu þessi tré inn í forgangsröðina. Því lægri sem tíðnin er, því meiri forgangur. Þannig er tréð með lægstu tíðnina alltaf valið við útdrátt.

Næst þarftu að gera eftirfarandi í hringrás:

  1. Sæktu tvö tré úr forgangsröðinni og gerðu þau að börnum nýs hnút (nýstofnaðan hnút án bókstafs). Tíðni nýja hnútsins er jöfn summan af tíðnum tveggja afkomandi trjáa.
  2. Fyrir þennan hnút skaltu búa til tré með rætur á þessum hnút. Settu þetta tré aftur inn í forgangsröðina. (Þar sem tréð hefur nýja tíðni mun það líklegast komast á nýjan stað í röðinni)
  3. Haltu áfram skrefum 1 og 2 þar til eitt tré er eftir í röðinni - Huffman tréð

Íhugaðu þetta reiknirit á línu s1:

Gagnaþjöppun með Huffman reikniritinu

Hér táknar táknið "lf" (línaföt) nýja línu, "sp" (bil) er bil.

Og hvað er næst?

Við fengum Huffman tréð. Allt í lagi. Og hvað á að gera við það? Þeir munu ekki taka það ókeypis. Og þá þarftu að rekja allar mögulegar leiðir frá rótinni til laufanna á trénu. Við erum sammála um að merkja brún 0 ef hún leiðir til vinstri barns og 1 ef hún leiðir til hægri. Strangt til tekið, í þessum merkingum, er stafakóði leiðin frá rót trésins að blaðinu sem inniheldur einmitt þennan staf.

Gagnaþjöppun með Huffman reikniritinu

Þannig kom kóðataflan út. Athugaðu að ef við skoðum þessa töflu getum við ályktað um "þyngd" hvers stafs - þetta er lengd kóðans. Síðan, í þjöppuðu formi, mun frumskráin vega: 2 * 3 + 2 * 4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 bitar . Í fyrstu vó það 176 bita. Þess vegna lækkuðum við það um allt að 176/65 = 2.7 sinnum! En þetta er útópía. Ólíklegt er að slíkt hlutfall fáist. Hvers vegna? Um þetta verður fjallað aðeins síðar.

Afkóðun

Jæja, kannski er það einfaldasta sem eftir er að afkóða. Ég held að mörg ykkar hafi giskað á að það sé ómögulegt að búa til þjappaða skrá án nokkurra vísbendinga um hvernig hún var umrituð - við munum ekki geta afkóða hana! Já, já, það var erfitt fyrir mig að átta mig á þessu, en ég þarf að búa til textaskrá table.txt með þjöppunartöflu:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

Taflafærsla á forminu 'stafur'"stafakóði". Af hverju er 01110 án tákns? Reyndar er það með tákni, bara java verkfærin sem ég nota þegar ég sendi út í skrá, nýlínustafurinn - 'n' - er breytt í nýja línu (sama hversu heimskulegt það hljómar). Þess vegna er tóma línan fyrir ofan stafurinn fyrir kóðann 01110. Fyrir kóðann 00 er stafurinn bil í upphafi línunnar. Ég verð að segja strax að þessi aðferð við að geyma borðið getur haldið því fram að hún sé óskynsamlegasta fyrir Khan stuðulinn okkar. En það er auðvelt að skilja og framkvæma. Ég mun vera fús til að heyra tillögur þínar í athugasemdum um hagræðingu.

Með þessari töflu er mjög auðvelt að afkóða. Við skulum muna hvaða reglu við höfðum að leiðarljósi þegar kóðunin var búin til:

Enginn kóði má vera forskeyti annars

Þetta er þar sem það gegnir auðvelda hlutverki. Við lesum smátt og smátt í röð og um leið og strengurinn d sem myndast, sem samanstendur af lesbitunum, passar við kóðun sem samsvarar stafstafnum, vitum við strax að stafstafurinn (og aðeins hann!) var kóðaður. Næst skrifum við staf í afkóðastrenginn (strengurinn sem inniheldur afkóðaðu skilaboðin), endurstillum d strenginn og lesum kóðuðu skrána frekar.

Framkvæmd

Það er kominn tími til að niðurlægja kóðann minn með því að skrifa skjalavörð. Við skulum kalla það Compressor.

Byrja aftur. Fyrst af öllu skrifum við Node flokkinn:

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

Nú tréð:

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

Forgangsröð:

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

Bekkurinn sem býr til Huffman tréð:

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

Flokkurinn sem inniheldur sem kóðar/afkóðar:

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

Bekkur sem auðveldar ritun í skrá:

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

Námskeið sem auðveldar lestur úr skrá:

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

Jæja, og aðalflokkurinn:

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

Þú verður að skrifa skrána með readme.txt leiðbeiningum sjálfur 🙂

Ályktun

Ég býst við að það væri allt sem ég vildi segja. Ef þú hefur eitthvað að segja um vanhæfni mína varðandi endurbætur á kóðanum, reikniritinu, almennt, hvaða hagræðingu sem er, þá skaltu ekki hika við að skrifa. Ef ég hef ekki útskýrt eitthvað, vinsamlegast skrifaðu líka. Mér þætti gaman að heyra frá þér í athugasemdunum!

PS

Já, já, ég er hér enn, því ég gleymdi ekki stuðlinum. Fyrir strenginn s1 vegur kóðunartaflan 48 bæti - miklu meira en upprunalega skráin, og þeir gleymdu ekki aukanúllunum (fjöldi bættra núlla er 7) => þjöppunarhlutfallið verður minna en eitt: 176 /(65 + 48*8 + 7) = 0.38. Ef þú tókst líka eftir þessu, þá ertu bara ekki búinn. Já, þessi útfærsla verður afar óhagkvæm fyrir litlar skrár. En hvað verður um stórar skrár? Skráarstærðin eru miklu stærri en kóðunartöflustærðin. Þetta er þar sem reikniritið virkar eins og það á að gera! Til dæmis, fyrir Einleikur Fausts skjalavörðurinn gefur raunverulegan (ekki hugsjón) stuðul sem jafngildir 1.46 - næstum einu og hálfu sinni! Og já, skráin átti að vera á ensku.

Heimild: www.habr.com

Bæta við athugasemd