🗜️

System.IO.Compressionの圧縮結果が.NET 4.5以降とそれ以前で異なる

2021/12/28に公開

Qiita を退会するにあたって記事の移植をおこないます。
元は2019年03月20日に投稿しました。
https://qiita.com/drafts/d7d09a91b468cd914ab9

System.IO.Compressionを使って圧縮

以下のようなコードでbyte[]を圧縮してみます

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;

namespace CsTest
{
    class Program
    {
        static byte[] CompressGZip(byte[] input)
        {
            using (var ms = new MemoryStream())
            {
                using (var compressStream = new GZipStream(ms, CompressionMode.Compress))
                    compressStream.Write(input, 0, input.Length);
                return ms.ToArray();
            }
        }
        static byte[] CompressDeflate(byte[] input)
        {
            using (var ms = new MemoryStream())
            {
                using (var compressStream = new DeflateStream(ms, CompressionMode.Compress))
                    compressStream.Write(input, 0, input.Length);
                return ms.ToArray();
            }
        }
        static void Main(string[] args)
        {
            var random = new Random(0);
            var binaries = new byte[][]{
                Enumerable.Range(0, 200).Select(i => (byte)i).ToArray(),
                Enumerable.Range(0, 100).SelectMany(i => new byte[]{ 0, (byte)i }).ToArray(),
                Enumerable.Repeat(0, 200).Select(_ => (byte)random.Next(byte.MaxValue)).ToArray(),
                Enumerable.Repeat(new byte[]{ 0, 20, 250, 24 }, 50).SelectMany(b => b).ToArray(),
            };

            foreach (var binary in binaries)
            {
                var gzResult = CompressGZip(binary);
                var deflateResult = CompressDeflate(binary);
                Console.WriteLine($"Original Length:{binary.Length} GZip Length: {gzResult.Length,3} Deflate Lengths: {deflateResult.Length,3}");
            }
        }
    }
}

実行結果

以下のように圧縮結果に差が出ました。

3.5

Original Length:200 GZip Length: 416 Deflate Lengths: 398
Original Length:200 GZip Length: 434 Deflate Lengths: 416
Original Length:200 GZip Length: 432 Deflate Lengths: 414
Original Length:200 GZip Length: 130 Deflate Lengths: 112

ターゲットフレームワーク

  • .NET Framework 3.5

で確認

4.5以降

Original Length:200 GZip Length: 223 Deflate Lengths: 205
Original Length:200 GZip Length: 157 Deflate Lengths: 139
Original Length:200 GZip Length: 223 Deflate Lengths: 205
Original Length:200 GZip Length:  27 Deflate Lengths:   9

ターゲットフレームワーク

  • .NET Framework 4.7.2
  • .NET Core 2.2

で確認

原因

以降では、 .NET Framework 4.5、DeflateStreamクラスは、zlib ライブラリを使用して圧縮します。 その結果、より優れた圧縮アルゴリズムを利用でき、ほとんどの場合、.NET Framework の以前のバージョンのものに比べて、より小さく圧縮されたファイルになります。
Starting with the .NET Framework 4.5.NET Framework 4.5, the DeflateStream class uses the zlib library for compression. As a result, it provides a better compression algorithm and, in most cases, a smaller compressed file than it provides in earlier versions of the .NET Framework.

https://docs.microsoft.com/ja-jp/dotnet/api/system.io.compression.gzipstream?view=netframework-4.7.2

公式のドキュメントに書いてあるとおりですが、4.5以降からはzlibを使っているためです。

備考

ターゲットフレームワークが.NET 4.0だった場合は、実行側が.NET 4.5以降をインストールしている場合は.NET 4.5以降での動作になるのでzlibによる圧縮になります。4.5以降がインストールされていなければ4.0の動作となりzlibではない(3.5と同じ)動作になるかと思います。

GitHubで編集を提案

Discussion