전 프로그래밍을 하다가 다른 라이브러리 사용 없이 압축을 할 수 있는 방법은 없을까 생각해본적이 있습니다.
파일 압축 말고도 데이터 압축(텍스트, 바이트 배열 등)에도 사용할 수 있는 압축 방법이 있다면 정말 좋은 소식이겠지요.
다행스럽게도, .NET의 네임스페이스에 있는 클래스를 이용하여 압축을 하고 풀 수가 있습니다.
모든 압축 방식을 지원하면 정말 좋겠지만, Deflate 및 GZIP 압축 알고리즘만을 지원합니다. 그리고, 스트림을 겸해서 사용하기 때문에 스트림이 익숙하지 않은 분들에겐 다소 불편할 수도 있겠지만, 익숙해지면 파일 스트림, 텍스트 스트림 등 모든 스트림을 다양하게 사용할 수 있게 되므로 일석 이조라고 생각하시면 될 것 같네요!
일단, 사용하려는 압축 스트림이 정의되어 있는 네임스페이스 입니다.
System.IO.Compression
이 네임스페이스에는 세 가지 형식이 정의되어 있습니다.
하나는 압축하는 방식을 나열하는 열거형 상수고, 나머지 두개는 압축/풀기 작업이 구현된 스트림 클래스입니다.
CompressionMode - 압축 방식을 나열하는 열거형 상수
DeflateStream - 압축 알고리즘을 이용한 압축 스트림
GZipStream - GZIP 압축 알고리즘을 이용한 압축 스트림
설명은 힘드니 바로 코드 설명을 해보도록 하겠습니다.
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace Example {
class Program {
public static void Main(string[] args) {
Console.Write("압축할 문자열을 입력하세요: ");
enterText:
String textToCompress = Console.ReadLine().Trim();
if ( String.IsNullOrEmpty(textToCompress) ) {
Console.WriteLine("다시 입력하세요.");
goto enterText;
}
Byte[] uniBytes = Encoding.Unicode.GetBytes(textToCompress);
Console.WriteLine("압축 전의 문자열 바이트: {0}", uniBytes.Length);
Byte[] compressedByte;
using ( MemoryStream ms = new MemoryStream() ) {
using ( DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress) ) {
ds.Write(uniBytes, 0, uniBytes.Length);
}
compressedByte = ms.ToArray();
Console.WriteLine("Deflate 압축 알고리즘을 사용하여 압축한 문자열 바이트: {0}", compressedByte.Length);
}
using ( MemoryStream ms = new MemoryStream() ) {
using ( GZipStream gs = new GZipStream(ms, CompressionMode.Compress) ) {
gs.Write(uniBytes, 0, uniBytes.Length);
}
compressedByte = ms.ToArray();
Console.WriteLine("GZip 압축 알고리즘을 사용하여 압축한 문자열 바이트: {0}", compressedByte.Length);
}
Console.ReadKey(true);
}
}
}
위 코드의 결과는 이렇습니다.
아주 중요하게 집고 넘어가야 할 것!
압축 스트림이 닫히는 순간, 압축 스트림으로 넘겨준 스트림 또한 닫히게 되므로 위의 예제에서 사용된 MemoryStream의 Read, Write 등 읽고 쓰기 메서드에 접근하는 순간 예외가 발생하게 됩니다.
그리고, 이 압축 스트림에 관한 예제는 MSDN 에서도 아주 자세하게 설명해주고 있습니다. (압축 스트림 사용 예제, MSDN)
다시 본론으로 돌아가서, 압축된 데이터의 압축을 푸는것은 정말 간단합니다.
압축 스트림을 생성할 때 CompressionMode 열거형의 값을 Compress 가 아닌 Decompress 로 주면 됩니다.
그리고, 압축 스트림의 데이터를 CopyTo 메서드를 이용하여 결과가 저장될 스트림으로 복사하면 되죠.
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace Example {
class Program {
public static void Main(string[] args) {
Console.Write("압축할 문자열을 입력하세요: ");
enterText:
String textToCompress = Console.ReadLine().Trim();
if ( String.IsNullOrEmpty(textToCompress) ) {
Console.WriteLine("다시 입력하세요.");
goto enterText;
}
Byte[] uniBytes = Encoding.Unicode.GetBytes(textToCompress);
Console.WriteLine("압축 전의 문자열 바이트: {0}", uniBytes.Length);
Byte[] compressedByte;
MemoryStream resultStream = new MemoryStream();
using ( MemoryStream ms = new MemoryStream() ) {
using ( DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress) ) {
ds.Write(uniBytes, 0, uniBytes.Length);
}
compressedByte = ms.ToArray();
Console.WriteLine("Deflate 압축 알고리즘을 사용하여 압축한 문자열 바이트: {0}", compressedByte.Length);
Console.WriteLine("압축된 문자열: {0}", Encoding.Unicode.GetString(compressedByte));
}
using ( MemoryStream ms = new MemoryStream(compressedByte) ) {
using ( DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress) ) {
ds.CopyTo(resultStream);
ds.Close();
}
resultStream.Position = 0;
Console.WriteLine("Deflate 압축 알고리즘을 사용하여 압축 해제한 문자열 바이트: {0}", resultStream.Length);
Console.WriteLine("압축 해제된 문자열: {0}", Encoding.Unicode.GetString(resultStream.ToArray()));
resultStream.Dispose();
resultStream = new MemoryStream();
}
using ( MemoryStream ms = new MemoryStream() ) {
using ( GZipStream gs = new GZipStream(ms, CompressionMode.Compress) ) {
gs.Write(uniBytes, 0, uniBytes.Length);
}
compressedByte = ms.ToArray();
Console.WriteLine("GZip 압축 알고리즘을 사용하여 압축한 문자열 바이트: {0}", compressedByte.Length);
Console.WriteLine("압축된 문자열: {0}", Encoding.Unicode.GetString(compressedByte));
}
using ( MemoryStream ms = new MemoryStream(compressedByte) ) {
using ( GZipStream gs = new GZipStream(ms, CompressionMode.Decompress) ) {
gs.CopyTo(resultStream);
gs.Close();
}
resultStream.Position = 0;
Console.WriteLine("GZip 압축 알고리즘을 사용하여 압축 해제한 문자열 바이트: {0}", resultStream.Length);
Console.WriteLine("압축 해제된 문자열: {0}", Encoding.Unicode.GetString(resultStream.ToArray()));
resultStream.Dispose();
}
Console.ReadKey(true);
}
}
}
압축 및 압축 해제가 추가된 예제 코드입니다.
이걸 메서드 형식으로 만들면 이렇게 짧게 바뀔 수 있고, 간단하게 호출도 가능합니다.
public static Byte[] Compress(Byte[] buffer) {
Byte[] compressedByte;
using ( MemoryStream ms = new MemoryStream() ) {
using ( DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress) ) {
ds.Write(buffer, 0, buffer.Length);
}
compressedByte = ms.ToArray();
}
return compressedByte;
}
public static Byte[] Decompress(Byte[] buffer) {
MemoryStream resultStream = new MemoryStream();
using ( MemoryStream ms = new MemoryStream(buffer) ) {
using ( DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress) ) {
ds.CopyTo(resultStream);
ds.Close();
}
}
Byte[] decompressedByte = resultStream.ToArray();
resultStream.Dispose();
return decompressedByte;
}
설명을 제대로 하려고는 하는데 필력이 좋지 않아서, 코드만 쭉쭉 써내려가네요.... ㅠㅠ
단계별로 차근차근 설명해 보도록 하겠습니다.
압축할 때:
- 압축 결과가 저장될 스트림 생성 (보통 MemoryStream을 사용)
- 1단계에서 생성한 스트림을 이용하여 압축 스트림 생성
- 압축 스트림에 압축할 데이터를 씀 (Write)
- 압축 결과가 저장될 스트림으로부터 압축된 데이터를 가져옴
- 압축 해제된 데이터가 저장될 스트림 생성
- 압축된 데이터를 이용하여 압축 해제 결과가 저장될 스트림을 생성
- 2단계에서 생성한 스트림을 이용하여 압축 스트림 생성 (압축 해제 모드)
- 생성된 압축 스트림의 값을 1단계에서 생성한 스트림에 복사
- 압축이 해제된 데이터가 저장된 스트림으로부터 압축 해제된 데이터를 가져옴
압축/압축 해제에 대한 설명을 마치도록 하겠습니다.
더 자세하게 설명해드리고 싶은데.. 필력이 안됩니다
긴 글 읽어주셔서 감사합니다!
'.NET > C#' 카테고리의 다른 글
대칭 알고리즘(SymmetricAlgorithm)을 이용한 데이터 암/복호화 하기 (0) | 2016.04.14 |
---|---|
base 키워드를 알아보자 (0) | 2015.06.14 |
예외 처리 (0) | 2014.10.23 |
바이트 배열로부터 폰트 만들기 (0) | 2014.10.22 |
문자열을 값으로 변환하기 (0) | 2014.10.20 |