GZipStreamはgzip形式の圧縮・展開(伸張)を行うためのストリーム。 .NET FrameworkではHttpWebResponseクラスがgzipで圧縮されたコンテンツ(Content-Encoding: gzip)を展開する場合にGZipStreamを使用するが、ファイルの圧縮などにも利用することができる。
GZipStreamは圧縮と展開のどちらにも対応しているが、インスタンス生成時に圧縮用か展開用かどちらか一方を指定する必要がある。 このストリームに対して書き込み・読み込みを行うことにより、gzip形式での圧縮・展開することができる。
類似のクラスとして、LZ77形式の圧縮・展開を行うためのDeflateStreamというクラスもある。 gzip形式・LZ77形式のいずれも可逆圧縮なので、当然圧縮前と展開後のデータのサイズは同じになる。
主なコンストラクタ
- GZipStream(Stream, CompressionMode)
- 圧縮・展開を行う対象のストリーム、圧縮か展開のどちらの操作を行うか指定してインスタンスを生成する。 圧縮用にストリームを作成する場合はCompressionMode.Compress、展開用にストリームを作成する場合はCompressionMode.Decompressを指定する。
- GZipStream(Stream, CompressionMode, Boolean)
- 圧縮/展開が終わってCloseしたあともストリームを開いたままにするかどうかを指定してインスタンスを生成する。 Trueを指定すると、GZipStreamを閉じてもベースとなるストリームは開かれたままになるので、圧縮・展開したストリームを使って追加の処理を行えるようストリームを再利用することができる。
主なメソッド
- void Write(byte[], int, int)
- 指定したバイト配列を圧縮し、ストリームに書き込む。
- int Read(byte[], int, int)
- ストリームから展開したデータを読み込み、指定したバイト配列に書き込む。
主な呼び出し順序
圧縮する場合。
- new GZipStream()でCompressionMode.Compressを指定
- Write()
- Close()
展開する場合。
- new GZipStream()でCompressionMode.Decompressを指定
- Read()
- Close()
使用例
gzファイルの書き込み・読み込み
GZipStreamを使って圧縮済みのテキストファイル(.txt.gz
)を書き込み・読み込みする。 未圧縮の一時ファイル(.txt
)を生成せず、直接圧縮済みのファイル(.txt.gz
)を読み書きする。 FileStreamクラスをGZipStreamクラスでラップすることにより、圧縮・展開を行った上でファイル内容の読み書きを行うことができる。
using System;
using System.IO;
using System.IO.Compression;
class Sample {
static void Main()
{
// 出力先のファイルを開く
using (var outputStream = File.Create("sample.txt.gz")) {
// 出力先のストリームへの書き込み内容をGZipStreamで圧縮する
using (var gzipStream = new GZipStream(outputStream, CompressionMode.Compress)) {
// GZipStreamへ書き込むStreamWriterを作成する
// (writerへ書き込む際、GZipStreamがその内容を圧縮した上でoutputStreamに書き込む)
using (var writer = new StreamWriter(gzipStream)) {
// writerを使って書き込む
writer.WriteLine("かごめかごめ");
writer.WriteLine("かごのなかのとりは");
writer.WriteLine("いついつでやる");
}
}
}
// 入力元のファイルを開く
using (var inputStream = File.OpenRead("sample.txt.gz")) {
// 入力元のストリームから読み込んだ内容をGZipStreamで展開する
using (var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress)) {
// GZipStreamから読み込むStreamReaderを作成する
// (readerから読み込む際、GZipStreamがその内容を展開した上でinputStreamから読み込む)
using (var reader = new StreamReader(gzipStream)) {
// readerを使って読み込む
Console.WriteLine(reader.ReadToEnd());
}
}
}
}
}
Imports System
Imports System.IO
Imports System.IO.Compression
Class Sample
Shared Sub Main()
' 出力先のファイルを開く
Using outputStream As FileStream = File.Create("sample.txt.gz")
' 出力先のストリームへの書き込み内容をGZipStreamで圧縮する
Using gzipStream As New GZipStream(outputStream, CompressionMode.Compress)
' GZipStreamへ書き込むStreamWriterを作成する
' (writerへ書き込む際、GZipStreamがその内容を圧縮した上でoutputStreamに書き込む)
Using writer As New StreamWriter(gzipStream)
' writerを使って書き込む
writer.WriteLine("かごめかごめ")
writer.WriteLine("かごのなかのとりは")
writer.WriteLine("いついつでやる")
End Using
End Using
End Using
' 入力元のファイルを開く
Using inputStream As FileStream = File.OpenRead("sample.txt.gz")
' 入力元のストリームから読み込んだ内容をGZipStreamで展開する
Using gzipStream As New GZipStream(inputStream, CompressionMode.Decompress)
' GZipStreamから読み込むStreamReaderを作成する
' (readerから読み込む際、GZipStreamがその内容を展開した上でinputStreamから読み込む)
Using reader As New StreamReader(gzipStream)
' readerを使って読み込む
Console.WriteLine(reader.ReadToEnd())
End Using
End Using
End Using
End Sub
End Class
gzip展開してファイル内容を表示するzcat
コマンドを用いて上記のコードが生成するsample.txt.gz
の内容を表示すると次のようになる。
$ zcat sample.txt.gz かごめかごめ かごのなかのとりは いついつでやる
この例のようにストリームを多段に重ねて使う方法についての詳細はストリームの基本とStreamクラス §.データフォーマットの変換やバッファリングなどの機能を追加するStream派生クラスを参照のこと。
ファイルの圧縮・展開
GZipStreamを使ってテキストファイルの内容を圧縮して別のファイルに保存する。 また、圧縮したファイルを再び展開して別のファイルに保存する。 この例で使用しているStream.CopyToメソッドは.NET Framework 4以降でないと使用できないので注意。
using System;
using System.IO;
using System.IO.Compression;
class Sample {
static void Main()
{
Console.WriteLine("[Compression]");
// 圧縮するファイルのFileStreamを作成する
using (var inputStream = File.OpenRead("sample.txt")) {
// 圧縮した結果を保存するファイルのFileStreamを作成する
using (var outputStream = File.Create("sample.txt.gz")) {
// データを圧縮してoutputStreamに書き込むためのGZipStreamを作成する
// (GZipStreamを閉じた後でもoutputStream.Lengthを参照できるよう、leaveOpenはtrueにする)
using (var gzipStream = new GZipStream(outputStream, CompressionMode.Compress, true)) {
#if STREAM_COPY
// inputStreamからデータを読み出し、gzipStreamに書き込む
inputStream.CopyTo(gzipStream);
#else
// .NET Framework 4より前のバージョンではCopyToメソッドを使うことはできないので以下のようにする
var buffer = new byte[1024];
for (;;) {
var len = inputStream.Read(buffer, 0, buffer.Length);
if (len == 0)
break;
gzipStream.Write(buffer, 0, len);
}
#endif
}
// 圧縮前後のファイルサイズを表示
Console.WriteLine("original: {0:N0} bytes", inputStream.Length);
Console.WriteLine("compressed: {0:N0} bytes ({1:P1})",
outputStream.Length, outputStream.Length / (double)inputStream.Length);
}
}
Console.WriteLine("[Decompression]");
// 展開するファイルのFileStreamを作成する
using (var inputStream = File.OpenRead("sample.txt.gz")) {
// inputStreamから読み込みデータを展開するためのGZipStreamを作成する
// (GZipStreamを閉じた後でもinputStream.Lengthを参照できるよう、leaveOpenはtrueにする)
using (var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress, true)) {
// 展開した結果を保存するファイルのFileStreamを作成する
using (var outputStream = File.Create("out.txt")) {
#if STREAM_COPY
// gzipStreamから展開されたデータを読み出し、outputStreamに書き込む
gzipStream.CopyTo(outputStream);
#else
// .NET Framework 4より前のバージョンではCopyToメソッドを使うことはできないので以下のようにする
var buffer = new byte[1024];
for (;;) {
var len = gzipStream.Read(buffer, 0, buffer.Length);
if (len == 0)
break;
outputStream.Write(buffer, 0, len);
}
#endif
// 展開前後のファイルサイズを表示
Console.WriteLine("original: {0:N0} bytes", inputStream.Length);
Console.WriteLine("decompressed: {0:N0} bytes ({1:P1})",
outputStream.Length, outputStream.Length / (double)inputStream.Length);
}
}
}
}
}
Imports System
Imports System.IO
Imports System.IO.Compression
Class Sample
Shared Sub Main()
Console.WriteLine("[Compression]")
' 圧縮するファイルのFileStreamを作成する
Using inputStream As FileStream = File.OpenRead("sample.txt")
' 圧縮した結果を保存するファイルのFileStreamを作成する
Using outputStream As FileStream = File.Create("sample.txt.gz")
' データを圧縮してoutputStreamに書き込むためのGZipStreamを作成する
' (GZipStreamを閉じた後でもoutputStream.Lengthを参照できるよう、leaveOpenはtrueにする)
Using gzipStream As New GZipStream(outputStream, CompressionMode.Compress, True)
#If STREAM_COPY
' inputStreamからデータを読み出し、gzipStreamに書き込む
inputStream.CopyTo(gzipStream)
#Else
' .NET Framework 4より前のバージョンではCopyToメソッドを使うことはできないので以下のようにする
Dim buffer(1024 - 1) As Byte
Do
Dim len As Integer = inputStream.Read(buffer, 0, buffer.Length)
If len = 0 Then Exit Do
gzipStream.Write(buffer, 0, len)
Loop
#End If
End Using
' 圧縮前後のファイルサイズを表示
Console.WriteLine("original: {0:N0} bytes", inputStream.Length)
Console.WriteLine("compressed: {0:N0} bytes ({1:P1})", _
outputStream.Length, outputStream.Length / inputStream.Length)
End Using
End Using
Console.WriteLine("[Decompression]")
' 展開するファイルのFileStreamを作成する
Using inputStream As FileStream = File.OpenRead("sample.txt.gz")
' inputStreamから読み込みデータを展開するためのGZipStreamを作成する
' (GZipStreamを閉じた後でもinputStream.Lengthを参照できるよう、leaveOpenはtrueにする)
Using gzipStream As New GZipStream(inputStream, CompressionMode.Decompress, true)
' 展開した結果を保存するファイルのFileStreamを作成する
Using outputStream As FileStream = File.Create("out.txt")
#If STREAM_COPY
' gzipStreamから展開されたデータを読み出し、outputStreamに書き込む
gzipStream.CopyTo(outputStream)
#Else
' .NET Framework 4より前のバージョンではCopyToメソッドを使うことはできないので以下のようにする
Dim buffer(1024 - 1) As Byte
Do
Dim len As Integer = gzipStream.Read(buffer, 0, buffer.Length)
If len = 0 Then Exit Do
outputStream.Write(buffer, 0, len)
Loop
#End If
' 展開前後のファイルサイズを表示
Console.WriteLine("original: {0:N0} bytes", inputStream.Length)
Console.WriteLine("decompressed: {0:N0} bytes ({1:P1})", _
outputStream.Length, outputStream.Length / inputStream.Length)
End Using
End Using
End Using
End Sub
End Class
[Compression] original: 3,208 bytes compressed: 941 bytes (29.3%) [Decompression] original: 941 bytes decompressed: 3,208 bytes (340.9%)
[Compression] original: 3,208 bytes compressed: 941 bytes (29.3%) [Decompression] original: 941 bytes decompressed: 3,208 bytes (340.9%)
同じ内容のファイルについて、GZipStreamの代わりにDeflateStreamを使用して実行した例。
[Compression] original: 3,208 bytes compressed: 923 bytes (28.8%) [Decompression] original: 923 bytes decompressed: 3,208 bytes (347.6%)
[Compression] original: 3,208 bytes compressed: 923 bytes (28.8%) [Decompression] original: 923 bytes decompressed: 3,208 bytes (347.6%)
ZIPアーカイブを作成したい場合は、System.IO.Compression.ZipArchiveクラスを用いることができる。
圧縮レベルの指定
.NET Framework 4.5からは、GZipStream・DeflateStreamのコンストラクタにCompressionLevel列挙体の値を指定できるようになっている。 これにより圧縮時の圧縮レベルを指定することができる。 GZipStream・DeflateStreamに指定できる圧縮レベルは次のとおり。
CompressionLevel | 圧縮レベル |
---|---|
CompressionLevel.Optimal | 圧縮率優先 |
CompressionLevel.Fastest | 圧縮速度優先 |
CompressionLevel.NoCompression | 無圧縮 |
GZipStreamにCompressionLevelを指定して圧縮した場合の所要時間、圧縮率の違いは次の通り。
E:\>sample.exe sample.dat input length: 681836 Optimal : 00:00:00.0433742 14.3% Fastest : 00:00:00.0179198 16.7% NoCompression : 00:00:00.0104490 100.0% E:\>sample.exe sample.txt input length: 3208 Optimal : 00:00:00.0010596 29.3% Fastest : 00:00:00.0002112 31.3% NoCompression : 00:00:00.0001500 100.7% E:\>sample.exe sample.avi input length: 76800512 Optimal : 00:00:07.3913581 43.2% Fastest : 00:00:03.4181816 47.2% NoCompression : 00:00:01.1296466 100.0%
using System;
using System.IO;
using System.IO.Compression;
using System.Diagnostics;
class Sample {
static void Main(string[] args)
{
using (var inputStream = File.OpenRead(args[0])) {
Console.WriteLine("input length: {0}", inputStream.Length);
// CompressionLevelの値を列挙
foreach (CompressionLevel compressionLevel in Enum.GetValues(typeof(CompressionLevel))) {
Console.Write("{0,-15}: ", compressionLevel);
// 初期サイズに元ファイルのサイズ分を確保してMemoryStreamを作成
using (var outputStream = new MemoryStream((int)inputStream.Length)) {
using (var gzipStream = new GZipStream(outputStream, compressionLevel, true)) {
var sw = Stopwatch.StartNew();
inputStream.Position = 0L;
inputStream.CopyTo(gzipStream);
// 圧縮にかかった時間を表示
Console.Write("{0} ", sw.Elapsed);
}
// 圧縮率を表示
Console.WriteLine("{0:P1}", outputStream.Length / (double)inputStream.Length);
}
}
}
}
}