Huffman الگورٿم استعمال ڪندي ڊيٽا کمپريشن

جائز آهي

هن آرٽيڪل ۾ آئون مشهور Huffman الگورتھم بابت ڳالهائيندس، انهي سان گڏ ڊيٽا کمپريشن ۾ ان جي ايپليڪيشن.

نتيجي طور، اسان هڪ سادي آرڪائيو لکندا سين. اهو اڳ ۾ ئي بحث ڪيو ويو آهي Habré تي مضمونپر عملي طور تي عمل ڪرڻ کان سواء. موجوده پوسٽ جو نظرياتي مواد اسڪول ڪمپيوٽر سائنس جي سبقن ۽ رابرٽ لافورٽ جي ڪتاب ”جاوا ۾ ڊيٽا جي جوڙجڪ ۽ الگورتھم“ مان ورتو ويو آهي. تنهن ڪري، هر شيء ڪٽي وئي آهي!

ڪجھ خيال

هڪ باقاعده ٽيڪسٽ فائل ۾، هڪ ڪردار 8 بٽس (ASCII انڪوڊنگ) يا 16 (يونيڪوڊ انڪوڊنگ) سان انڪوڊ ٿيل آهي. اڳيون اسان ASCII انڪوڊنگ تي غور ڪنداسين. مثال طور، ليڪ وٺو s1 = “SUSIE SAYS IT IS ASYn”. لڪير ۾ ڪل 22 اکر آهن، قدرتي طور تي، جن ۾ خال ۽ نئين لڪير جو ڪردار - 'n' شامل آهي. هن لڪير تي مشتمل فائل جو وزن 22*8 = 176 بِٽ هوندو. سوال فوري طور تي پيدا ٿئي ٿو: ڇا اهو منطقي آهي سڀني 8 بٽس کي استعمال ڪرڻ لاءِ 1 ڪردار کي انڪوڊ ڪرڻ لاءِ؟ اسان سڀ ASCII اکر استعمال نٿا ڪريون. جيتوڻيڪ اهي ڪندا هئا، اهو وڌيڪ منطقي هوندو ته سڀ کان وڌيڪ عام اکر - S - کي تمام ننڍو ممڪن ڪوڊ ڏنو وڃي، ۽ ناياب خط - T (يا U، يا 'n') - لاء هڪ ڊگهو ڪوڊ ڏنو وڃي. اھو اھو آھي جيڪو Huffman الورورٿم تي مشتمل آھي: اھو ضروري آھي ته بھترين انڪوڊنگ اختيار کي ڳولھيو جنھن ۾ فائل کي گھٽ ۾ گھٽ وزن ھوندو. اهو بلڪل عام آهي ته ڪوڊ جي ڊيگهه مختلف علامتن لاءِ مختلف هوندي - هي اهو آهي جيڪو الورورٿم تي ٻڌل آهي.

ڪوڊنگ

ڇو نه ڪردار 'S' کي ڪوڊ ڏيو، مثال طور، 1 سا ڊگھو: 0 يا 1. ان کي 1 ٿيڻ ڏيو. پوء ٻيو سڀ کان عام اکر - '' (اسپيس) - 0 ڏيو. تصور ڪريو ته توھان پنھنجي پيغام کي ڊيڪوڊ ڪرڻ شروع ڪيو - انڪوڊ ٿيل اسٽرنگ s1 - ۽ توهان ڏسو ٿا ته ڪوڊ 1 سان شروع ٿئي ٿو. پوء، توهان ڇا ڪندا: ڇا اهو S آهي، يا اهو ڪو ٻيو ڪردار آهي، مثال طور A؟ تنهن ڪري، هڪ اهم قاعدو پيدا ٿئي ٿو:

ڪو به ڪوڊ ٻئي جو اڳوڻو نه هجڻ گهرجي

هي قاعدو الورورٿم ۾ اهم آهي. تنهن ڪري، ڪوڊ ٺاهڻ هڪ فريڪوئنسي ٽيبل سان شروع ٿئي ٿو، جيڪو هر علامت جي تعدد (واقعات جو تعداد) ظاهر ڪري ٿو:

Huffman الگورٿم استعمال ڪندي ڊيٽا کمپريشن سڀ کان وڌيڪ واقعن سان ڪردارن کي گهٽ ۾ گهٽ انڪوڊ ڪيو وڃي ممڪن بٽس جو تعداد. مان هڪ ممڪن ڪوڊ جدولن مان هڪ مثال ڏيندس:

Huffman الگورٿم استعمال ڪندي ڊيٽا کمپريشن تنهن ڪري انڪوڊ ٿيل پيغام هن طرح نظر ايندو:

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

مون هر ڪردار جي ڪوڊ کي اسپيس سان الڳ ڪيو. اهو نه ٿيندو هڪ واقعي ٺهيل فائل ۾!
سوال ٿو پيدا ٿئي ته هي نوجوان ڪيئن آيو ڪوڊ سان گڏ ڪوڊ جي ٽيبل ٺاهي؟ هن هيٺ بحث ڪيو ويندو.

هڪ Huffman وڻ جي تعمير

اهو آهي جتي بائنري ڳولا جا وڻ بچاء لاء ايندا آهن. پريشان نه ٿيو، توهان کي هتي طريقن جي ڳولا، داخل ڪرڻ، يا حذف ڪرڻ جي ضرورت نه پوندي. هتي جاوا ۾ وڻ جي جوڙجڪ آهي:

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

هي مڪمل ڪوڊ نه آهي، مڪمل ڪوڊ هيٺ هوندو.

هتي وڻ جي تعمير لاء الگورتھم آهي:

  1. پيغام مان هر ڪردار لاء نوڊ اعتراض ٺاهيو (لائن s1). اسان جي حالت ۾ 9 نوڊس (نوڊ اعتراض) هوندا. هر نوڊ ٻن ڊيٽا فيلڊن تي مشتمل آهي: علامت ۽ تعدد
  2. هر نوڊ لاءِ هڪ وڻ اعتراض (BinaryTree) ٺاهيو. نوڊ وڻ جي پاڙ بڻجي ويندو آهي.
  3. انهن وڻن کي ترجيحي قطار ۾ داخل ڪريو. گھٽ تعدد، اعلي ترجيح. اهڙيء طرح، جڏهن ڪڍڻ، سڀ کان گهٽ تعدد سان dervo هميشه چونڊيو ويندو آهي.

اڳيون، توھان کي ھيٺ ڏنل cyclically ڪرڻ جي ضرورت آھي:

  1. ترجيحي قطار مان ٻن وڻن کي هٽايو ۽ انھن کي نئين نوڊ (اکر کان سواءِ نئون ٺهيل نوڊ) جو اولاد بڻايو. نئين نوڊ جي تعدد ٻن نسلن جي وڻن جي تعدد جي رقم جي برابر آهي.
  2. ھن نوڊ لاءِ، ھن نوڊ تي روٽ سان ھڪڙو وڻ ٺاھيو. ھن وڻ کي واپس ترجيحي قطار ۾ داخل ڪريو. (جيئن ته وڻ کي نئين تعدد آهي، اهو گهڻو ڪري قطار ۾ نئين جاء تي ظاهر ٿيندو)
  3. قدم 1 ۽ 2 کي جاري رکو جيستائين قطار ۾ صرف ھڪڙو وڻ رهجي وڃي - ھفمن وڻ

ھن الگورتھم تي غور ڪريو لائن s1 تي:

Huffman الگورٿم استعمال ڪندي ڊيٽا کمپريشن

هتي علامت "lf" (linefeed) جو مطلب آهي هڪ نئين لائين، "sp" (space) هڪ خلا آهي.

ا What'sتي What'sا آهي؟

اسان کي هڪ Huffman وڻ مليو. ٺيڪ. ۽ ان سان ڇا ڪجي؟ اهي مفت ۾ به نه وٺندا، ۽ پوء، توهان کي وڻ جي پاڙ کان پنن تائين هر ممڪن رستو ڳولڻ جي ضرورت آهي. اچو ته هڪ ڪنڊ 0 کي ظاهر ڪرڻ تي متفق آهيون جيڪڏهن اهو کاٻي ٻار ڏانهن وٺي وڃي ٿو ۽ 1 جيڪڏهن اهو ساڄي طرف وڃي ٿو. سختي سان ڳالهائڻ، هن اشاري ۾، علامت جو ڪوڊ وڻ جي پاڙ کان پني تائين رستو آهي جنهن ۾ اهو ئي علامت آهي.

Huffman الگورٿم استعمال ڪندي ڊيٽا کمپريشن

هن طريقي سان ڪوڊ جو جدول نڪتو. ياد رهي ته جيڪڏهن اسان هن جدول تي غور ڪريون ٿا، اسان هر علامت جي "وزن" بابت نتيجو ڪري سگهون ٿا - اهو ان جي ڪوڊ جي ڊيگهه آهي. ان کان پوء، ٺهيل شڪل ۾، اصل فائل جو وزن ٿيندو: 2 * 3 + 2 * 4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 بٽ . شروعات ۾ ان جو وزن 176 بٽ هو. انڪري، اسان ان کي گھٽائي ڇڏيو 176/65 = 2.7 ڀيرا! پر هي هڪ يوٽوپيا آهي. اهڙو ڪوفيشيٽ حاصل ڪرڻ ممڪن ناهي. ڇو؟ اهو ٿوري دير کان پوء بحث ڪيو ويندو.

ڊيڪوڊنگ

خير، شايد سادو شيءِ ڇڏي وئي آهي ڊيڪوڊنگ. مان سمجهان ٿو ته توهان مان ڪيترن ئي اندازو لڳايو آهي ته اسان صرف هڪ ڪمپريس فائل ٺاهي نٿا سگهون بغير ڪنهن اشاري جي اهو ڪيئن انڪوڊ ڪيو ويو - اسان ان کي ڊيڪوڊ ڪرڻ جي قابل نه هوندا! ها، ها، اهو محسوس ڪرڻ منهنجي لاءِ مشڪل هو، پر مون کي هڪ ٽيڪسٽ فائل table.txt ٺاهڻو پوندو هڪ ڪمپريشن ٽيبل سان:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

ٽيبل جي داخلا فارم ۾ 'علامت' 'ڪردار ڪوڊ'. 01110 ڪنهن علامت کان سواءِ ڇو آهي؟ حقيقت ۾، اهو هڪ علامت سان آهي، اهو صرف اهو آهي ته جاوا اوزار جيڪي آئون استعمال ڪريان ٿو جڏهن فائل کي ٻاھر ڪڍو، نئين لائن ڪردار - 'n' - نئين لائن ۾ تبديل ٿي وڃي ٿو (جيتوڻيڪ اھو ڪيترو بيوقوف ٿي سگھي ٿو). تنهن ڪري، مٿي تي خالي لڪير ڪوڊ 01110 لاءِ اکر آهي. ڪوڊ 00 لاءِ، اکر لائن جي شروعات ۾ هڪ جاءِ آهي. مان فوري طور تي چوندس ته اسان جي خان جي کوٽائي لاء، ٽيبل کي محفوظ ڪرڻ جو هي طريقو سڀ کان وڌيڪ غير معقول هجڻ جي دعوي ڪري سگهي ٿو. پر اهو سمجهڻ ۽ عمل ڪرڻ آسان آهي. مان توهان جي تجويزون ٻڌي خوش ٿيندس اصلاح جي حوالي سان تبصرن ۾.

هي ٽيبل هجڻ ڪري ان کي ڊيڪوڊ ڪرڻ تمام آسان بڻائي ٿو. اچو ته ياد رکون ته انڪوڊنگ ٺاهڻ وقت اسان ڪهڙي قاعدي تي عمل ڪيو:

ڪو به ڪوڊ ٻئي جو اڳوڻو نه هجڻ گهرجي

اهو آهي جتي اهو هڪ سهولت وارو ڪردار ادا ڪري ٿو. اسان ترتيبوار بِٽ بِٽ پڙهون ٿا ۽ جيئن ئي نتيجي ۾ نڪرندڙ اسٽرنگ ڊي، جنهن ۾ پڙهيل بِٽ شامل آهن، انڪوڊنگ سان ملن ٿا انڪوڊنگ جي ڪردار سان ملندڙ جلندڙ، اسان کي فوري طور تي معلوم ٿئي ٿو ته اکر جو ڪردار (۽ صرف اهو!) انڪوڊ ڪيو ويو هو. اڳيون، اسين ڪردار کي ڊيڪوڊنگ لائن ۾ لکون ٿا (جنهن ۾ ڊيڪوڊ ٿيل پيغام شامل آهي)، لائن ڊي کي ري سيٽ ڪريو، ۽ پوء انڪوڊ ٿيل فائل کي پڙهو.

عمل

اهو وقت آهي منهنجي ڪوڊ کي ذليل ڪرڻ ۽ هڪ آرڪيور لکڻ جو. اچو ته ان کي ڪمپريسر سڏين.

ٻيهر شروع. سڀ کان پهريان، اسان نوڊ ڪلاس لکندا آهيون:

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

هاڻي وڻ:

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

ترجيحي قطار:

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

ڪلاس جيڪو 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;
    }
}

ڪلاس جنهن ۾ انڪوڊ/ڊيڪوڊ شامل آهن:

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

ھڪڙو طبقو جيڪو فائل ۾ لکڻ آسان بڻائي ٿو:

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

ھڪڙو ڪلاس جيڪو فائل مان پڙھڻ آسان بڻائي ٿو:

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

خير، ۽ مکيه طبقو:

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

توهان کي پڙهڻو پوندو readme.txt فائل پاڻ کي :)

ٿڪل

مان سمجهان ٿو ته اهو سڀ ڪجهه چوڻ چاهيان ٿو. جيڪڏهن توهان وٽ ڪوڊ، الگورٿم، يا عام طور تي ڪنهن به اصلاح کي بهتر ڪرڻ ۾ منهنجي نااهلي بابت ڪجهه چوڻ آهي، ته پوءِ آزاد محسوس ڪريو. جيڪڏهن مون ڪجهه وضاحت نه ڪئي آهي، مهرباني ڪري پڻ لکو. مان تبصرن ۾ توهان کان ٻڌڻ چاهيان ٿو!

پي ايس

ها، ها، مان اڃا تائين هتي آهيان، ڇاڪاڻ ته مون کي کوٽائي جي باري ۾ نه وساريو. اسٽرنگ s1 لاءِ، انڪوڊنگ ٽيبل جو وزن 48 بائيٽ آھي - ماخذ فائل کان گھڻو وڏو، ۽ اسان اضافي زيرو (شامل ٿيل زيرو جو تعداد 7 آھي) جي باري ۾ نه وساريو => ڪمپريشن تناسب ھڪ کان گھٽ ھوندو: 176/ (65 + 48*8 + 7) = 0.38. جيڪڏهن توهان اهو پڻ محسوس ڪيو، ته اهو صرف توهان جو چهرو نه آهي جيڪو عظيم آهي. ها، هي عمل درآمد ننڍي فائلن لاءِ انتهائي غير موثر هوندو. پر وڏي فائلن کي ڇا ٿيندو؟ فائل جي سائيز انڪوڊنگ ٽيبل جي سائيز کان تمام وڏا آھن. هي اهو آهي جتي الورورٿم ڪم ڪري ٿو جيئن ان کي گهرجي! مثال طور، لاء فاسٽ جو مونالوگ آرڪائيور 1.46 جو حقيقي (مثالي نه ڪيل) کوٽائي پيدا ڪري ٿو - لڳ ڀڳ هڪ ۽ اڌ ڀيرا! ۽ ها، فائيل انگريزيءَ ۾ هجڻ گهرجي ها.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو