전 프로그래밍을 하다가 다른 라이브러리 사용 없이 압축을 할 수 있는 방법은 없을까 생각해본적이 있습니다.

파일 압축 말고도 데이터 압축(텍스트, 바이트 배열 등)에도 사용할 수 있는 압축 방법이 있다면 정말 좋은 소식이겠지요.


다행스럽게도, .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단계에서 생성한 스트림에 복사
  • 압축이 해제된 데이터가 저장된 스트림으로부터 압축 해제된 데이터를 가져옴


압축/압축 해제에 대한 설명을 마치도록 하겠습니다.

더 자세하게 설명해드리고 싶은데.. 필력이 안됩니다


긴 글 읽어주셔서 감사합니다!

+ Recent posts