1

こんにちは、独自のハフマン コーディングを作成するプロジェクトに取り組んでいます。現在、バイナリの 1 と 0 を出力ファイルに書き込めません。小さい入力ファイルでは機能しますが、非常に大きなファイルの場合、出力ファイルには何も書き出されません。書き込みを担当するメソッドはcompressメソッドです。どんな助けでも大歓迎です。ありがとうございました!

package proj3;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.PriorityQueue;

public class Project3 {

//variables for PriorityQueue and Huffman Tree
private static PriorityQueue<BinaryNode<Character>> queue;
private static BinaryNode<Character> huffTree;
private static Map<Character, String> table = new LinkedHashMap<Character, String>();

/**
 * Method for creating Huffman Tree
 * @param counts Map that contains all characters and their frequencies
 * @return the Huffman Tree
 */
public static BinaryNode<Character> makeTree(Map<Character, Integer> counts)
{
    queue = new PriorityQueue<BinaryNode<Character>>();

    for(Character c : counts.keySet())
    {
        BinaryNode<Character> tree = new BinaryNode<Character>(c, counts.get(c), null, null);
        queue.add(tree);
    }

    while(!queue.isEmpty())
    {
        if(queue.size() >= 2)
        {   
            BinaryNode<Character> n1 = queue.remove();
            BinaryNode<Character> n2 = queue.remove();
            Integer weight = n1.getFreq() + n2.getFreq();
            huffTree = new BinaryNode<Character>(null, weight, n1, n2);
            queue.add(huffTree);
        }
        if(queue.size() == 1)
        {
            return queue.remove();
        }
    }
    return huffTree;
}

public static void encode(BinaryNode<Character> node, String s)
{
    if(!node.isLeaf())
    {
        encode(node.getLeft(), s + "0");
        encode(node.getRight(), s + "1");
    }
    else
    {
        table.put(node.getElement(), s);
    }
}

public static void compress(String in, String out) throws IOException
{
    try 
    {
        File outFile = new File(out);
        FileOutputStream compressedFile = new FileOutputStream(outFile);
        Scanner infile = new Scanner(new FileInputStream(in));
        while(infile.hasNext())
        {
            infile.useDelimiter("");
            String str = infile.next();
            Character character = str.charAt(0);
            for(Character c : table.keySet())
            {
                if(c == character){
                    compressedFile.write(table.get(c).getBytes());
                    compressedFile.flush();
                }
            }
        }
        for(Byte b : table.get('^').getBytes())
        {
            compressedFile.write(b);
        }
        infile.close();
        compressedFile.close();
    }
    catch (FileNotFoundException e) 
    {
        System.err.println("File not found.");
        e.printStackTrace();
    }
}

public static void decompress(String s)
{

}

public static void printEncodings(Map<Character, String> m)
{
    ArrayList<Character> chars = new ArrayList<Character>();

    System.out.println("Character Encodings");
    System.out.println("---------------------");
    for(Character c : m.keySet())
    {
        chars.add(c);
        Collections.sort(chars);
    }
    for(Character c : chars)
    {
        System.out.print(c + "\t" + m.get(c) + "\n");
    }
    System.out.println();
    System.out.println("Total Characters: " + chars.size());
}

/**
 * Method for creating map with character and its frequencies
 * @param s the file name to be opened
 * @return the Map containing characters and frequencies
 */
public static Map<Character, Integer> charCount(String s){

    Map<Character, Integer> counts = new LinkedHashMap<Character, Integer>();
    ArrayList<Character> chars = new ArrayList<Character>();

    try {
        Scanner file = new Scanner(new FileInputStream(s));
        while(file.hasNext()){
            file.useDelimiter("");
            String str = file.next();
            Character c = str.charAt(0);
            if(counts.containsKey(c)){
                counts.put(c, counts.get(c) + 1);
            }
            else{
                counts.put(c, 1);
            }
        }
        counts.put('^', 1);
        System.out.println("Character Frequencies");
        System.out.println("---------------------");
        for(Character c : counts.keySet())
        {
            chars.add(c);
            Collections.sort(chars);
        }
        for(Character c : chars){
            System.out.println(c + "\t" + counts.get(c));
        }
        System.out.println();
        System.out.println("Total characters: " + chars.size() + "\n");
        file.close();
    } 
    catch (FileNotFoundException e) {
        System.err.println("File not found.");
        System.exit(0);
    }
    return counts;
}

public static void main(String[] args){

    if(args.length != 3)
    {
        throw new IllegalArgumentException("Invalid number of arguments.");
    }
    encode(makeTree(charCount(args[0])), "");
    printEncodings(table);
    try {
        compress(args[0], args[1]);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}
4

2 に答える 2

0

compress() メソッドでパフォーマンス/メモリの問題に直面していると確信しています。エンコーディングはメインメソッドで問題なく出力されますが、ファイル出力が動かなくなると思います。コードの最適化には、少なくとも 3 つの可能性があります。

  1. Scanner を使用して入力ファイルを文字単位で読み取りますが、 Scannerが提供する解析機能は使用しません。代わりにInputStreamReaderを使用してみてください。

  2. ハフマン テーブル内の一連のキーをループして、等しいかどうかをチェックしています。現在の文字を使用して、マップされている文字列を取得し、ループを省略できます。

  3. バイト配列をループして出力ファイルに書き込む必要はありません。FileOutputStreamの write() メソッドは、バイト配列全体をパラメーターとして受け取ることができます。

私の謙虚なJavaスキルでは、以下のように実装したいと思います。BinaryNode クラスがないため、これはテストされていないコードであることに注意してください。

public static void compress(String in, String out) throws IOException
{
    try 
        {
            File outFile = new File(out);
            FileOutputStream compressedFile = new FileOutputStream(outFile);

            // 1. Use a Reader instead of a Scanner;
            // make sure to use the correct charset
            FileInputStream fis= new FileInputStream(in);
            Reader reader = new InputStreamReader(fis,        
                Charset.forName("US-ASCII"));

            // use BufferedReader for even better performance
            Reader bufferedReader = new BufferedReader(reader);

            int r;
            while ((r = bufferedReader.read()) != -1) {
                char ch= (char) r;

                // 2. Get the string for this character directly instead of
                // looping the keySet and checking for equivalence
                String s= table.get(ch);
                if (s != null) {
                    // 3. Write entire array of bytes instead of 
                    // looping and writing bytes one by one
                    compressedFile.write(s.getBytes());
                }
            }
            fis.close();
            compressedFile.close();
        }
    ...
于 2013-04-27T21:47:48.900 に答える
0

電話する必要がある可能性が高いcompressedFile.flush()

このように改善します

            if(c == character){
                compressedFile.write(table.get(c).getBytes());
                compressedFile.flush();
            }

さらに、 の使用を検討してtry/catchください。実装の整合性が向上します。

于 2013-04-27T21:07:20.327 に答える