DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

GZIPやデフレートでファイルを圧縮する

注意:ここで紹介している方法は、.NET Framework 2.0以降でのみ有効です。.NET Framework 1.1以前では、こちらで紹介している#ziplibを使って行うことができます。

.NET Framework 2.0から追加されたSystem.IO.Compression名前空間には、データを圧縮するためのクラスが用意されています。.NET Framework 2.0では、GZIP形式を使うGZipStreamクラスと、デフレートアルゴリズムを使うDeflateStreamクラスが用意されています。

.NET Framework 3.5以前では、これらのクラスは、4GB以下のデータの圧縮にのみ使用できます。.NET Framework 4.0からは、この制限はなくなったようです。

GZipStreamクラスを使ってGZIPでファイルを圧縮、展開する

ファイルを圧縮する

まずはGZipStreamクラスを使ってファイルを圧縮する例を紹介します。以下のコードでは、"C:\test.txt"ファイルをGZIPで圧縮して、"C:\test.txt.gz"という書庫を作成しています。

VB.NET
コードを隠すコードを選択
'圧縮するファイルのパス
Dim compFile As String = "C:\test.txt"
'作成する圧縮ファイルのパス
Dim gzipFile As String = compFile + ".gz"

'圧縮するデータをすべて読み取る
Dim inFileStrm As New System.IO.FileStream( _
    compFile, System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim compData(inFileStrm.Length - 1) As Byte
inFileStrm.Read(compData, 0, compData.Length)
inFileStrm.Close()

'作成する圧縮ファイルのFileStreamを作成する
Dim compFileStrm As New System.IO.FileStream( _
    gzipFile, System.IO.FileMode.Create)
'圧縮モードのGZipStreamを作成する
Dim gzipStrm As New System.IO.Compression.GZipStream( _
    compFileStrm, System.IO.Compression.CompressionMode.Compress)
'データを圧縮して書き込む
gzipStrm.Write(compData, 0, compData.Length)
'閉じる
gzipStrm.Close()
C#
コードを隠すコードを選択
//圧縮するファイルのパス
string compFile = @"C:\test.txt";
//作成する圧縮ファイルのパス
string gzipFile = compFile + ".gz";

//圧縮するデータをすべて読み取る
System.IO.FileStream inFileStrm = new System.IO.FileStream(
    compFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
byte[] compData = new byte[inFileStrm.Length];
inFileStrm.Read(compData, 0, compData.Length);
inFileStrm.Close();

//作成する圧縮ファイルのFileStreamを作成する
System.IO.FileStream compFileStrm =
    new System.IO.FileStream(gzipFile, System.IO.FileMode.Create);
//圧縮モードのGZipStreamを作成する
System.IO.Compression.GZipStream gzipStrm =
    new System.IO.Compression.GZipStream(compFileStrm,
        System.IO.Compression.CompressionMode.Compress);
//データを圧縮して書き込む
gzipStrm.Write(compData, 0, compData.Length);
//閉じる
gzipStrm.Close();
補足:上記のサンプルでは分かりやすさを優先しているため、ファイルの内容を一度に全て読み込んでから、圧縮しています。しかしこの方法では、大きなファイルを圧縮する時に大量のメモリを消費してしまいます。これを防ぐには、圧縮するファイルの中身を少しずつ読み込んで、GZipStreamに書き込むようにします。そのように修正したサンプルを、以下に示します。
VB.NET
コードを隠すコードを選択
'圧縮するファイルのパス
Dim compFile As String = "C:\test.txt"
'作成する圧縮ファイルのパス
Dim gzipFile As String = compFile + ".gz"

'作成する圧縮ファイルのFileStreamを作成する
Dim compFileStrm As New System.IO.FileStream( _
    gzipFile, System.IO.FileMode.Create)
'圧縮モードのGZipStreamを作成する
Dim gzipStrm As New System.IO.Compression.GZipStream( _
    compFileStrm, System.IO.Compression.CompressionMode.Compress)

'圧縮するファイルを開いて少しずつbufferに読み込む
Dim inFileStrm As New System.IO.FileStream( _
    compFile, System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim buffer(1024) As Byte
While True
    '圧縮するファイルからデータを読み込む
    Dim readSize As Integer = inFileStrm.Read(buffer, 0, buffer.Length)
    '最後まで読み込んだ時は、ループを抜ける
    If readSize = 0 Then
        Exit While
    End If 'データを圧縮して書き込む
    gzipStrm.Write(buffer, 0, readSize)
End While

'閉じる
inFileStrm.Close()
gzipStrm.Close()
C#
コードを隠すコードを選択
//圧縮するファイルのパス
string compFile = @"C:\test.txt";
//作成する圧縮ファイルのパス
string gzipFile = compFile + ".gz";

//作成する圧縮ファイルのFileStreamを作成する
System.IO.FileStream compFileStrm =
    new System.IO.FileStream(gzipFile, System.IO.FileMode.Create);
//圧縮モードのGZipStreamを作成する
System.IO.Compression.GZipStream gzipStrm =
    new System.IO.Compression.GZipStream(compFileStrm,
        System.IO.Compression.CompressionMode.Compress);

//圧縮するファイルを開いて少しずつbufferに読み込む
System.IO.FileStream inFileStrm = new System.IO.FileStream(
    compFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
byte[] buffer = new byte[1024];
while (true)
{
    //圧縮するファイルからデータを読み込む
    int readSize = inFileStrm.Read(buffer, 0, buffer.Length);
    //最後まで読み込んだ時は、ループを抜ける
    if (readSize == 0)
        break;
    //データを圧縮して書き込む
    gzipStrm.Write(buffer, 0, readSize);
}

//閉じる
inFileStrm.Close();
gzipStrm.Close();
私が試した限りでは、圧縮するファイルの中身を全て読み込んでから圧縮した時と比べ、少しずつ読み込んで圧縮した時の方が、書庫ファイルのサイズが若干大きくなりました。展開はどちらでも問題なく正常にできました。

GZipStreamクラスは、データ(ストリームやバイト型配列)を圧縮するだけのものです。ファイルを圧縮する時にファイル名などの情報を書庫に格納するようなことはしません。そのため、圧縮したファイル名の最後に「.gz」を付けて、元のファイル名が分かるようにするのが一般的です。

このような訳で、この方法では複数のファイルを圧縮して一つの書庫に格納することはできません。複数ファイルを圧縮する方法については、後述します。

このようにして作成した書庫は、tar32.dllなどのアーカイバで展開することができます。

ファイルを展開(解凍、圧縮解除)する

次に、上記のような方法で圧縮したファイルを展開する方法を紹介します。この例では、"C:\test.txt.gz"という書庫ファイルを展開して、"C:\test1.txt"というファイルを作成しています

VB.NET
コードを隠すコードを選択
'展開する書庫のパス
Dim gzipFile As String = "C:\test.txt.gz"
'展開先のファイル名
Dim outFile As String = "C:\test1.txt"

'展開する書庫のFileStreamを作成する
Dim gzipFileStrm As New System.IO.FileStream( _
    gzipFile, System.IO.FileMode.Open, System.IO.FileAccess.Read)
'圧縮解除モードのGZipStreamを作成する
Dim gzipStrm As New System.IO.Compression.GZipStream( _
    gzipFileStrm, System.IO.Compression.CompressionMode.Decompress)
'展開先のファイルのFileStreamを作成する
Dim outFileStrm As New System.IO.FileStream( _
    outFile, System.IO.FileMode.Create, System.IO.FileAccess.Write)

Dim buffer(1024) As Byte
While True
    '書庫から展開されたデータを読み込む
    Dim readSize As Integer = gzipStrm.Read(buffer, 0, buffer.Length)
    '最後まで読み込んだ時は、ループを抜ける
    If readSize = 0 Then
        Exit While
    End If '展開先のファイルに書き込む
    outFileStrm.Write(buffer, 0, readSize)
End While

'閉じる
outFileStrm.Close()
gzipStrm.Close()
C#
コードを隠すコードを選択
//展開する書庫のパス
string gzipFile = @"C:\test.txt.gz";
//展開先のファイル名
string outFile = @"C:\test1.txt";

//展開する書庫のFileStreamを作成する
System.IO.FileStream gzipFileStrm = new System.IO.FileStream(
    gzipFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
//圧縮解除モードのGZipStreamを作成する
System.IO.Compression.GZipStream gzipStrm =
    new System.IO.Compression.GZipStream(gzipFileStrm,
        System.IO.Compression.CompressionMode.Decompress);
//展開先のファイルのFileStreamを作成する
System.IO.FileStream outFileStrm = new System.IO.FileStream(
    outFile, System.IO.FileMode.Create, System.IO.FileAccess.Write);

byte[] buffer = new byte[1024];
while (true)
{
    //書庫から展開されたデータを読み込む
    int readSize = gzipStrm.Read(buffer, 0, buffer.Length);
    //最後まで読み込んだ時は、ループを抜ける
    if (readSize == 0)
        break;
    //展開先のファイルに書き込む
    outFileStrm.Write(buffer, 0, readSize);
}

//閉じる
outFileStrm.Close();
gzipStrm.Close();

DeflateStreamクラスを使ってデフレートでファイルを圧縮、展開する

DeflateStreamクラスを使ってファイルを圧縮、展開する方法は、GZipStreamクラスを使った方法と全く同じです。上記のGZipStreamクラスを使った例の"GZipStream"を"DeflateStream"に置き換えるだけでOKです。

なお、圧縮率はどちらもあまり変わらないようです。

複数のファイルを圧縮する

先に述べたように、ここで紹介した方法では、複数のファイルを圧縮することができません。複数のファイルを圧縮するためには、圧縮する全てのファイルを一つのファイル(データ)にまとめて、圧縮する必要があります。

一般的には、複数のファイルをTARでまとめてからGZIPで圧縮するという方法がよく使われ、「.TAR.GZ」または「.TGZ」形式などと呼ばれます。TARでファイルをまとめるには、#ziplibなどを使います。

このような既存の方法を使うのでなければ、複数のファイルを一つにまとめる独自の規則を作成する必要があります。このような方法で複数のファイルを圧縮するサンプルがMSDNの「圧縮のアプリケーション サンプル」にあります。また、「How to compress folders and multiple files with GZipStream and C#」でもサンプルが紹介されています。

ここで紹介した方法で複数のファイルを圧縮するのは面倒ですので、こちらで紹介しているような方法でZIPで圧縮したほうが楽でしょう。

  • 履歴:
  • 2009/8/20 VB.NETのコードの「Dim compData(inFileStrm.Length) As Byte」を「Dim compData(inFileStrm.Length - 1) As Byte」に修正。
  • 2016/6/12 .NET Framework 4.0から4GBの制限がなくなったことを追記(コメントで教えていただきました)。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • Windows Vista以降でUACが有効になっていると、ファイルへの書き込みに失敗する可能性があります。詳しくは、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。