データの圧縮・展開をするコードと、思わずはまってしまったところ

ちょっと仕事で、データの圧縮・展開をやらないといけなくなったので、付け焼刃で勉強してテストプログラムを作るところまでやった。下はひとまずの完成品。

Imports System
Imports System.IO
Imports System.IO.Compression

Module CompDecompTest
    Private Function Compress(ByVal byteArray() As Byte) As Byte()
        Dim ms As New MemoryStream
        Dim zipStream As New GZipStream(ms, CompressionMode.Compress, True)
        zipStream.Write(byteArray, 0, byteArray.Length)
        zipStream.Close()
        ms.Close()
        Return ms.ToArray()
    End Function

    Private Function Decompress(ByRef byteArray() As Byte) As Byte()
        Dim ms As New MemoryStream(byteArray)
        Dim zipStream As New GZipStream(ms, CompressionMode.Decompress)
        Dim decompByteArray(1000) As Byte
        Dim totalBytes As Integer = 0
        Dim offset As Integer = 0
        Dim bytesRead As Integer = 0
        Do
            If (offset + 1000) > decompByteArray.Length Then
                Array.Resize(decompByteArray, offset + 1000)
            End If
            bytesRead = zipStream.Read(decompByteArray, offset, 1000)
            offset += bytesRead
            totalBytes += bytesRead
        Loop While bytesRead <> 0
        Array.Resize(decompByteArray, totalBytes)
        Return decompByteArray
    End Function

    Sub Main()
        Dim testData() As Byte = {}
        Array.Resize(testData, 10000)
        Dim dataGenerator As New Random
        dataGenerator.NextBytes(testData)
        Dim compData() = Compress(testData)
        System.Console.WriteLine("original size {0} bytes.compressed size {1} bytes.", testData.Length, compData.Length)
        Dim decompData() = Decompress(compData)
        System.Console.WriteLine("compressed size {0} bytes.decompressed size {1} bytes.", compData.Length, decompData.Length)
    End Sub

実はこれを実行すると、圧縮処理前と後では圧縮後の方が大きくなるという恐ろしい結果になるw まー、ほぼランダムな数列は圧縮するのが難しい。入力をテキストにしてやれば圧縮!って感じになります。まあ、そこらへんは適当にやっていただくとして。

今回、こーゆーコードになったわけなんだけど、今日2時間ほどうなったバグがあった。それはCompress関数の中のzipStream.Close()とms.Close()。このストリームのCloseをやらないでToArrayすると中途半端な圧縮結果が出てきてしまう。それをそのまんまDecompress関数に渡すと、妙な事にGZipStreamのRead関数が一発目で読み取りサイズゼロを返す。意味するところはストリームのおしまい。ReadByte関数に書き換えてもマイナス1が帰ってきた。でもね、もう一回がんばると値を返してくるんだよw これでわけがわからなくてかなり悩んだ。原因はDecompressの中ではなくて、そもそもCompressで得たByte配列が不完全なものだったというオチ。きちんとStreamを閉じてから出ないとToArrayがマトモな値を返さない。この点に注意が必要。

ふー、やっぱ普段使い慣れないモノを使うといろいろと落とし穴にはまるなあ。