IT/소스코드

[Java] Huffman Coding 1/2

zzun 2003. 7. 10. 06:04
반응형
공학수학2 / 2002년 2학기 / 박근수 교수님

[설명]
Huffman Coding : 각 문자의 Frequency를 이용한 텍스트 압축
Huffenc.java : 입력텍스트(Huffman.in)로부터 압축파일(Huffman.cmp)과 빈도파일(Huffman.frq) 생성
Huffdec.java : 압축파일과 빈도파일로부터 출력텍스트(Huffman.out) 생성
HuffmanTree.java : 코딩&디코딩에 사용되는 트리

[Huffenc.java]
import java.io.*;

class HuffOutputStream                                // 비트단위 출력을 담당하는 클래스
{
       public HuffOutputStream(OutputStream os) throws IOException
       {
               buffer = 0;                                        // 한 바이트를 읽어 저장하는 버퍼
               position = 0;                                // 현재 비트가 바이트 내에서 몇번째 비트인지를 가리킴.
               out = new DataOutputStream(os);
       }

       public void writeCode(String code) throws IOException                // 0과 1의 스트링으로 된 코드를 출력
       {
               for (int i=0; i<code.length(); i++)
                       writeBit(code.charAt(i));                                                        // 한 비트씩 출력
       }

       private void writeBit(char c) throws IOException                        // 0 또는 1의 한 비트 출력
       {
               if (c == '1')
                       buffer |= (1<<position);                                                        // '00000001' 을 포지션만큼 왼쪽으로 쉬프트 시켜서 버퍼에 OR 시킴.
               else if (c != '0')
                       throw new IOException("Wrong Code!");
               position++;                                                                                                // 포지션을 한 칸 이동.
               if (position == 8)                                                                                // 마지막 비트까지 채웠으면 파일에 씀
                       flush();
       }

       public void flush() throws IOException                                                // 한 바이트가 되었을때 파일에 씀.
       {
               if (position == 0)                                                                                // 만약 한비트도 써지지 않았으면 아무것도 쓰지 않음
           return;
       out.write(buffer);
       position = 0;                                                                                        // 포지션 초기화
       buffer = 0;                                                                                                // 버퍼 초기화
       }

       public void close() throws IOException
       {
               flush();                                                                                                // 현재까지 쓰여진 1바이트가 안된 비트들을 파일에 씀
               out.close();
       }

       private DataOutputStream out;
       private int buffer;
       private int position;
}

public class Huffenc
{
       public static String inputFileName = "Huffman.in";
       public static String freqFileName = "Huffman.frq";
       public static String outputFileName = "Huffman.cmp";

       public static void main(String[] args) throws IOException
       {
               HuffmanTree tree = new HuffmanTree();

               InputStream fin = new BufferedInputStream(new FileInputStream(inputFileName));
               tree.charCount(fin);                                                                                        // 문자들의 프리퀀시를 셈
               fin.close();

               DataOutputStream freqOut = new DataOutputStream(new FileOutputStream(freqFileName));
               tree.writeFreq(freqOut);                                                                                // 프리퀀시 파일을 씀
               freqOut.close();
               
               fin = new BufferedInputStream(new FileInputStream(inputFileName));                // 한번 더 읽기 위해 다시 open
               HuffOutputStream fout = new HuffOutputStream(new FileOutputStream(outputFileName));
               int buf = fin.read();                                                                                        // 한 글자 읽음
               while (buf != -1)                                                                                                // 파일이 끝날때까지
               {
                       fout.writeCode(tree.getCode(buf));                                                        // 글자를 코드로 변환해서 출력
                       buf = fin.read();                                                                                        // 다시 한 글자 읽음
               }
               fout.writeCode(tree.getCode(tree.EOF));                                                        // 마지막임을 뜻하는 문자 쓰기
               fout.close();
       }
}

[Huffdec.java]
import java.io.*;

class HuffInputStream                                        // 비트단위 입력을 담당하는 클래스
{
       public HuffInputStream(InputStream is) throws IOException
       {
               position = 8;                                        // 현재 비트의 위치 기본값 지정
               in = new DataInputStream(is);   // 바이너리 인풋
       }

       public int readCode(HuffmanTree tree) throws IOException
       {
       String code = "";                                // 코드를 저정할 스트링
       int bit = readBit();                        // 한 비트를 읽어서 저장 (0 또는 1)
               int decodedKey;

       while(true)
       {
           code += bit;                                                                        // 스트링에 방금 읽은 비트 추가
                       decodedKey = tree.getKey(code);                                        // 지금 까지 스트링을 decode 해본다
                       if (bit==-1 || decodedKey==tree.ERROR)                        // EOF가 나오기 전에 파일이 끝나거나 디코딩 에러가 날 경우
                       {
                               System.out.println("Decoding Error!");
                               System.exit(0);
                       }
                       else if (decodedKey == tree.NOT_A_VALUE)                // 아직 하나의 코드가 아닐 경우
                       {
                               bit = readBit();                                                        // 새로운 비트를 읽고 다음 loop을 실행
                               continue;
                       }
                       else
                               return decodedKey;                                                                // 제대로 디코딩 되었으면 디코드한 결과를 리턴
               }
       }

       private int readBit() throws IOException                                // 한 비트를 읽음
       {
               if ( position == 8 )                                                                // 현재 남은 비트가 없으면 (혹은 처음이면)
       {
           buffer = in.read();                                                                // 한 바이트를 읽어서
           if( buffer == -1 )                                                                // 끝이면 끝이라고 리턴
               return -1;
           position = 0;                                                                        // 포지션을 맨 앞으로
       }

               return (buffer & (1<<position++)) == 0 ? 0 : 1;                // '00000001'을 포지션만큼 왼쪽으로 쉬프트시켜서 버퍼랑 AND 시킨 결과가 현재 비트!
       }

       public void close() throws IOException
       {
               in.close();
       }

       private DataInputStream in;
       private int buffer;
       private int position;
}

public class Huffdec
{
       public static String inputFileName = Huffenc.outputFileName;
       public static String freqFileName = Huffenc.freqFileName;
       public static String outputFileName = "Huffman.out";

       public static void main(String[] args) throws IOException
       {
               HuffmanTree tree = new HuffmanTree();
               DataInputStream freqIn = new DataInputStream(new FileInputStream(freqFileName));
               tree.readFreq(freqIn);                                                                                                                                // 프리퀀시 파일을 읽음
               freqIn.close();

               HuffInputStream fin = new HuffInputStream(new FileInputStream(inputFileName));
               OutputStream fout = new BufferedOutputStream(new FileOutputStream(outputFileName));
               int currentChar = fin.readCode(tree);                                                                                        // 디코드된 글자 하나를 읽음
               while (currentChar != tree.EOF)                                                                        // 마지막 이전까지
               {
                       fout.write(currentChar);                                                                                        // 글자를 씀
                       currentChar = fin.readCode(tree);                                                                        // 다시 글자 하나를 읽음
               }
               fin.close();
               fout.close();
       }
}
반응형