د هفمن الګوریتم په کارولو سره د معلوماتو کمپریشن

د ننوتلو

پدې مقاله کې به زه د مشهور هفمن الګوریتم په اړه وغږیږم، او همدارنګه د ډیټا کمپریشن کې د هغې غوښتنلیک.

د پایلې په توګه، موږ به یو ساده آرشیور ولیکئ. دا لا دمخه بحث شوی دی د Habré په اړه مقالهمګر د عملي تطبیق پرته. د اوسني پوسټ نظري مواد د ښوونځي کمپیوټر ساینس درسونو او د رابرټ لافورټ کتاب "په جاوا کې د معلوماتو جوړښتونه او الګوریتم" څخه اخیستل شوي. نو، هرڅه پرې شوي!

یو څو فکرونه

په منظم متن فایل کې، یو کرکټر د 8 بټونو (ASCII کوډ کولو) یا 16 (یونیکوډ کوډ کولو) سره کوډ شوی. بیا به موږ د ASCII کوډ کولو ته پام وکړو. د مثال په توګه، د s1 کرښه واخلئ = "SUSIE وايي چې دا اسانه ده". په کرښه کې ټولټال 22 حروف شتون لري، په طبیعي توګه، په شمول د ځایونو او د نوې کرښې کرکټر - 'n'. هغه فایل چې دا کرښه لري وزن به یې 22*8 = 176 بټونه وي. پوښتنه سمدلاسه راپورته کیږي: ایا دا معقوله ده چې د 8 کرکټر کوډ کولو لپاره ټول 1 بټونه وکاروئ؟ موږ ټول ASCII حروف نه کاروو. حتی که دوی یې کړي وي، دا به د خورا عام لیک - S - لپاره خورا معقول وي چې ترټولو لنډ ممکن کوډ ورکړل شي، او د نایاب لیک - T (یا U، یا 'n') لپاره - اوږد کوډ ورکړل شي. دا هغه څه دي چې د هفمن الګوریتم لري: دا اړینه ده چې د کوډ کولو غوره انتخاب ومومئ په کوم کې چې فایل به لږترلږه وزن ولري. دا خورا عادي خبره ده چې د کوډ اوږدوالی به د مختلف سمبولونو لپاره توپیر ولري - دا هغه څه دي چې الګوریتم پراساس دی.

کوډ کول

ولې 'S' کرکټر ته کوډ نه ورکوئ، د مثال په توګه، 1 بټ اوږد: 0 یا 1. پریږدئ چې دا 1 وي. بیا دویم خورا عام کرکټر - '' (ځای) - 0 ورکړئ. تصور وکړئ چې تاسو د خپل پیغام کوډ کول پیل کړي - کوډ شوی تار s1 - او تاسو ګورئ چې کوډ له 1 سره پیل کیږي. نو تاسو څه کوئ: ایا دا S کرکټر دی ، یا دا کوم بل کرکټر دی ، د مثال په توګه A؟ له همدې امله، یو مهم قاعده رامنځته کیږي:

هیڅ کوډ باید د بل مختګ نه وي

دا قاعده په الګوریتم کې کلیدي ده. له همدې امله، د کوډ جوړول د فریکونسۍ جدول سره پیل کیږي، کوم چې د هر سمبول فریکونسۍ (د پیښو شمیر) په ګوته کوي:

د هفمن الګوریتم په کارولو سره د معلوماتو کمپریشن هغه کرکټرونه چې ډیری پیښې لري باید لږترلږه کوډ شي امکان لري د بټونو شمیر. زه به د ممکنه کوډ جدولونو څخه یو مثال ورکړم:

د هفمن الګوریتم په کارولو سره د معلوماتو کمپریشن نو کوډ شوی پیغام به داسې ښکاري:

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

ما د هر کرکټر کوډ د ځای سره جلا کړ. دا به په ریښتیا کمپریس شوي فایل کې پیښ نشي!
پوښتنه راپورته کیږي: دا ځوان څنګه کوډ سره راغلی ترڅو د کوډونو جدول رامینځته کړي؟ دا به لاندې بحث وشي.

د هفمن ونې جوړول

دا هغه ځای دی چې د بائنری لټون ونې ژغورنې ته راځي. اندیښنه مه کوئ، تاسو به دلته د لټون، داخلولو، یا حذف کولو میتودونو ته اړتیا ونلرئ. دلته په جاوا کې د ونې جوړښت دی:

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 تل غوره کیږي.

بیا تاسو اړتیا لرئ لاندې په سایکل ډول ترسره کړئ:

  1. دوه ونې د لومړیتوب له کتار څخه لرې کړئ او د نوي نوډ (نوي جوړ شوي نوډ پرته له خط څخه) ماشومان جوړ کړئ. د نوي نوډ فریکونسۍ د دوو نسلي ونو د فریکونسۍ له مجموعې سره مساوي ده.
  2. د دې نوډ لپاره، په دې نوډ کې د ریښې سره یوه ونه جوړه کړئ. دا ونه بیرته د لومړیتوب کتار کې دننه کړئ. (ځکه چې ونه نوې فریکونسۍ لري، نو دا به ډیر احتمال په کتار کې په نوي ځای کې ښکاره شي)
  3. 1 او 2 مرحلې ته دوام ورکړئ تر هغه چې په قطار کې یوازې یوه ونه پاتې وي - د هفمن ونه

دا الګوریتم په لاین s1 کې په پام کې ونیسئ:

د هفمن الګوریتم په کارولو سره د معلوماتو کمپریشن

دلته د "lf" (linefeed) سمبول د نوې کرښې معنی لري، "sp" (space) یو ځای دی.

او نور څه دي؟

موږ د هفمن ونه ترلاسه کړه. سمه ده. او څه ورسره وکړو؟ دوی حتی دا وړیا نه اخلي او بیا، تاسو اړتیا لرئ چې د ونې تر پاڼو پورې ټولې ممکنه لارې ومومئ. راځئ موافقه وکړو چې یوه څنډه 0 په ګوته کړو که چیرې دا کیڼ ماشوم ته لار ومومي او 1 که دا ښي لور ته وګرځي. په کلکه ووایو، په دې یادښت کې، د سمبول کوډ د ونې له ریښې څخه پاڼي ته لاره ده چې دا سمبول لري.

د هفمن الګوریتم په کارولو سره د معلوماتو کمپریشن

دا څنګه د کوډ جدول وګرځید. په یاد ولرئ چې که موږ دا جدول په پام کې ونیسو، موږ کولی شو د هر سمبول "وزن" په اړه پایله وکړو - دا د دې کوډ اوږدوالی دی. بیا، په کمپریس شوي بڼه کې، اصلي فایل به وزن ولري: 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 کوډ لپاره، کرکټر د کرښې په پیل کې یو ځای دی. زه به سمدلاسه ووایم چې زموږ د خان کثافاتو لپاره، د میز ذخیره کولو دا طریقه ادعا کولی شي خورا غیر منطقي وي. مګر دا د پوهیدو او پلي کولو لپاره اسانه ده. زه به خوښ شم چې د اصلاح کولو په اړه نظرونو کې ستاسو وړاندیزونه واورم.

د دې میز درلودل د کوډ کولو لپاره خورا اسانه کوي. راځئ چې په یاد ولرو چې موږ د کوډ کولو په وخت کې کوم قواعد تعقیب کړل:

هیڅ کوډ باید د بل مختګ نه وي

دا هغه ځای دی چې دا د اسانتیا رول لوبوي. موږ په ترتیب سره یو په بل پسې لوستل کوو او هرڅومره ژر چې پایله لرونکی تار d چې د لوستلو بټونو څخه جوړ دی د کریکټ کرکټر سره ورته کوډ کولو سره سمون لري ، موږ سمدلاسه پوهیږو چې د کرکټر کرکټر (او یوازې دا!) کوډ شوی و. بیا، موږ د کوډ کولو لاین کې کرکټر لیکو (هغه کرښه چې د کوډ شوي پیغام لري)، لاین d بیا تنظیم کړئ، او بیا کوډ شوی فایل ولولئ.

پلي کول

دا وخت دی چې زما کوډ ته سپکاوی وکړم او آرشیور ولیکئ. راځئ چې دا کمپرسور ووایو.

له سره پیل کول. لومړی، موږ د نوډ ټولګي لیکو:

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

هغه ټولګی چې د هفمن ونه رامینځته کوي:

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 فایل پخپله ولیکئ :)

پایلې

زما په اند دا ټول هغه څه دي چې ما غوښتل ووایم. که تاسو په عمومي توګه د کوډ، الګوریتم، یا کوم اصلاح کولو کې زما د بې کفایتۍ په اړه څه ووایاست، نو د لیکلو لپاره وړیا احساس وکړئ. که ما څه نه وي تشریح کړي، مهرباني وکړئ هم ولیکئ. زه غواړم په نظرونو کې ستاسو څخه واورم!

PS

هو، هو، زه لاهم دلته یم، ځکه چې ما د کوفینټ په اړه هیر نه کړ. د سټینګ s1 لپاره، د کوډ کولو میز 48 بایټ وزن لري - د سرچینې فایل څخه خورا لوی، او موږ د اضافي صفرونو په اړه هیر نکړو (د اضافه صفر شمیر 7 دی) => د کمپریشن تناسب به له یو څخه کم وي: 176/ (65 + 48*8 + 7) = 0.38. که تاسو دا هم په پام کې نیولې وي، نو دا یوازې ستاسو مخ نه دی چې عالي دی. هو، دا تطبیق به د کوچنیو فایلونو لپاره خورا غیر موثر وي. مګر د لویو فایلونو سره څه کیږي؟ د فایل اندازه د کوډ کولو میز د اندازې څخه خورا لوی دي. دا هغه ځای دی چې الګوریتم کار کوي لکه څنګه چې باید وي! د مثال په توګه، لپاره د فاوست مونولوګ آرشیور د 1.46 ریښتیني (نه مثالی) کثافات تولیدوي - نږدې یو نیم ځله! او هو، فایل باید په انګلیسي کې وي.

سرچینه: www.habr.com

Add a comment