제5단계 인코딩하기
압축파일의 맨 앞부분(header)에 파일(원본)을 구성하는 run들에 대한 정보를 기록한다.
이 때 원본 파일의 길이도 함께 기록한다. (왜 필요할까?)
: 각각의 run 들에게 어떤 codeword를 부여했는지 알아야 복원 가능하기 때문에 codeword에 대한 정보를 기록한다.
아니면 frequency에 대한 정보를 저장한다.
outputFrequencies
private void outputFrequencies(RandomAccessFile fIn,
RandomAccessFile fOut) throws IOException {
//fIn : 압축할 파일
//fOut : 압축된 파일
fOut.writeInt(runs.size());
// 먼저 run의 개수를 하나의 정수로 출력한다.
fOut.writeLong(fIn.getFilePointer());
// 원본 파일의 크기(byte 단위)를 출력한다.
for (int j = 0; j < runs.size(); j++) {
//
Run r = runs.get(j);
fOut.write(r.symbol); // write a byte
fOut.writeInt(r.runLen);
fOut.writeInt(r.freq);
}
}
compressFile
public void compressFile(String inFileName, RandomAccessFile fIn)
//fin은 압축할 파일, inFileName은 그 파일 이름이다. (압축 파일명 정하기 가능)
throws IOException {
String outFileName = new String(inFileName+”.z");
// 압축 파일의 이름은 압축할 파일의 이름에 확장자를 . z를 붙인 것이다.
RandomAccessFile fOut = new RandomAccessFile(outFileName,”rw");
//압축파일을 여기서 생성하여 outputFrequncies와 encode 메소드에서 제공한다.
collectRuns(fIn);
outputFrequencies(fIn,fOut);
createHuffmanTree();
assignCodewords(theRoot, 0, 0);
storeRunsIntoArray(theRoot);
fIn.seek(0);
encode(fIn, fOut); //중요!
}
public class HuffmanCoding {
…
public void compressFile(String inFileName, RandomAccessFile fIn)
throws IOException {
…
}
static public void main (String args[]) {
HuffmanCoding app = new HuffmanCoding();
RandomAccessFile fIn;
try {
fIn = new RandomAccessFile(“sample.txt”,”r");
app.compressFile(“sample.txt”, fIn);
fIn.close();
} catch (IOException io) {
System.err.println("Cannot open " + fileName);
}
}
}
encode()
- encode를 위하여 하나의 buffer를 사용한다.
* buffer는 32비트 정수를 사용하여 구현한다.
1) 앞에서부터 데이터파일을 읽어서 하나의 run 을 인식한다.
2) run을 인식하여 값을 할당한 뒤 buffer에 채운다.
3) buffer 에 만약에 빈 자리가 run에 할당된 값 만큼 없으면
file 에 output 한 뒤 buffer를 비우고, 다시 채운다
예시 : 1100110* 같이 1자리 남은 상태에서 111 이 할당될 경우
1100111 로 채우고 buffer를 채우고 다시 새로운 버퍼에 00000011 같이
11 남은 것을 마저 채우고 또 shift 해서 110을 넣을 경우
00011110 과 같이 만든다.
encode
private void encode(RandomAccessFile fIn, RandomAccessFile fOut) {
while there remains bytes to read in the file {
recognise a run;
find the codeword for the run;
//HashMap - map.get(newRun, symbol, runlength);
//임시적 run 객체를 생성하여 그걸 key로 해서 hashmap에 저장한다.
pack the codeword into the buffer;
if the buffer becomes full
write the buffer into the compressed file;
}
if buffer is not empty {
append 0s into the buffer;
write the buffer into the compressed file;
}
}
파일 끝에 도달해서 while문을 빠져나왔을 시,
buffer가 full이 안 되었다면 아직 몇 bit의 코드가 남아있을 수 있다.
그러면 잔여 공간에 0을 채워서 한 바이트로 만들어 주고 마지막으로 output 할 수 있게 한다.
class HuffmanEncoder
public class HuffmanEncoder {
static public void main (String args[]) {
String fileName = "";
HuffmanCoding app = new HuffmanCoding();
RandomAccessFile fIn;
Scanner kb = new Scanner(System.in);
try {
System.out.print("Enter a file name: ");
fileName = kb.next();
fIn = new RandomAccessFile(fileName,"r");
app.compressFile(fileName,fIn);
fIn.close();
} catch (IOException io) {
System.err.println("Cannot open " + fileName);
}
}
}
메인 함수 내에 삽입하지 않고 이제 HuffmanEncoder로 class를 생성하고,
filename도 입력받을 수 있게 한다.
(내용은 똑같은데 encode 함수 추가로 인해서 encoder랑 decoder 분리하기 위함)
댓글
댓글 쓰기