Zip for WPF/Silverlight
メモリでのデータの圧縮
チュートリアル > メモリでのデータの圧縮

このチュートリアルでは、文字列や double などの基本データ型をメモリストリームに圧縮する方法、およびストリームからデータを読み取る際の展開方法について説明します。最終アプリケーションは、次の図のように表示されます。

手順1:メインフォームを作成します。

Visual Studio プロジェクトで新しい WPF/Silverlight プロジェクトを作成します。ツールボックスから、ドラッグ&ドロップ操作を実行するか、コンポーネントをダブルクリックして、次のコントロールをフォームに追加します。

手順2:C1.WPF.Zip/C1.Silverlight.Zip アセンブリに参照を追加します。

ソリューションエクスプローラウィンドウに移動し、[すべてのファイルを表示]ボタンをクリックします。[参照]を右クリックし、[参照の追加]メニューオプションを選択します。リストから C1.WPF.Zip/C1.Silverlight.Zip アセンブリを選択するか、ファイルを参照して C1.WPF.Zip.dll/C1.Silverlight.Zipdll ファイルを探します。

[MainPage.xaml.vb]タブ(C# では[MainPage.xaml.cs]タブ)を選択するか、[表示]→[コード]を選択して、コードエディタを開きます。ファイルの上部に、次のステートメントを追加します。

コードのコピー
Imports System.IO
Imports C1.C1Zip
コードのコピー
using System.IO;
using C1.C1Zip;

これで、C1.WPF.Zip/C1.Silverlight.Zip アセンブリで定義されているオブジェクトがプロジェクトから可視になり、タイピング量を大きく減らすことができます。

手順3:文字列を圧縮するコードを追加します。

[文字列の圧縮]コマンドボタンをダブルクリックし、btnCompressString_Click イベントを処理する次のコードを追加します。

コードのコピー
Private m_CompressedString As Byte()
Private Sub btnCompressString_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCompressString.Click
    ' 文字列を圧縮します。
    Dim ticks As Long = DateTime.Now.Ticks
    m_CompressedString = CompressString(textBox1.Text)
    ' 処理にかかる時間をユーザーに通知します。
    Dim ms As Integer
    ms = (DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond
    Dim lenBefore As Integer = textBox1.Text.Length * 2
    Dim lenAfter As Integer = m_CompressedString.Length
    Dim msg As String
    msg = String.Format("Compressed from {0} bytes to " &  "{1} bytes in {2} milliseconds.", lenBefore, lenAfter, ms)
    MessageBox.Show(msg, "Compressed", MessageBoxButtons.OK, MessageBoxIcon.Information)
    ' これで、展開することができます。
    btnExpandString.Enabled = True
End Sub
コードのコピー
private byte[] _compressedString;
      private void btnCompressString_Click(object sender, RoutedEventArgs e)
        {
            // 文字列を圧縮します。
            long ticks = DateTime.Now.Ticks;
            richTextBox1.SelectAll();
            string text = richTextBox1.Selection.Text;
            _compressedString = CompressString(text);
            // 処理にかかる時間をユーザーに通知します。
            int ms = (int)((DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond);
            int lenBefore = text.Length * 2;
            int lenAfter = _compressedString.Length;
            string msg = string.Format("Compressed from {0} bytes to " + "{1} bytes in {2} milliseconds.", lenBefore, lenAfter, ms);
            MessageBox.Show(msg, "Compressed", MessageBoxButton.OK);
            // これで、展開することができます。
            btnExpandString.IsEnabled = true;
        }

最初に重要な行は、m_CompressedString というメンバ変数の宣言です。これは、圧縮データ(バイト配列としてエンコードされる)を保持するために使用されます。次に重要な行は、ユーティリティ関数 CompressString の呼び出しです。これは、指定された文字列をバイト配列に圧縮します。これを後から展開して、元の文字列を復元できます。残りのコードは、圧縮プロセスにかかる時間を計算し、その値をダイアログボックスに表示するために使用されます。

lenBefore 変数は(文字列の長さ)×2で計算されています。これは、.NET 文字列が Unicode であり、各文字が実際は2バイトあるためです。

CompressString 関数を実装する次のコードを追加します。

コードのコピー
Public Function CompressString(ByVal str As String) As Byte()
    ' メモリストリームを開きます。
    Dim ms As MemoryStream = New MemoryStream()
    ' 圧縮プログラムストリームをメモリストリームにアタッチします。
    Dim sw As C1ZStreamWriter = New C1ZStreamWriter(ms)
    ' データを圧縮プログラムストリームに書き込みます。
    Dim writer As StreamWriter = New StreamWriter(sw)
    writer.Write(str)
    ' 保留中のデータをフラッシュします。
    writer.Flush()
    ' メモリバッファを返します。
    CompressString = ms.ToArray()
End Function
コードのコピー
public byte[] CompressString(string str)
{
    // メモリストリームを開きます。
    MemoryStream ms = new MemoryStream();
    // 圧縮プログラムストリームをメモリストリームにアタッチします。
    C1ZStreamWriter sw = new C1ZStreamWriter(ms);
    // データを圧縮プログラムストリームに書き込みます。
    StreamWriter writer = new StreamWriter(sw);
    writer.Write(str);
   // 保留中のデータをフラッシュします。
    writer.Flush();
    // メモリバッファを返します。
    return ms.ToArray();
}

この関数は、最初に新しいメモリストリームを作成します。このストリームにより、圧縮データを保持するメモリバッファが自動的に割り当てられます。

次に、C1ZStreamWriter オブジェクトを作成し、それを新しいメモリストリームにアタッチします。C1ZStreamWriter オブジェクトに書き込まれたデータはすべて、圧縮されてメモリストリームに書き込まれます。

C1ZStreamWriter オブジェクトは、バイトデータとバイト配列を書き込むための基本ストリームメソッドを提供するだけです。文字列や整数などの他の基本型を書き込むには、StreamWriter オブジェクトを C1ZStreamWriter にアタッチします。次の図に、この関数の動作を示します。

StreamWriter の設定が終了したら、後はその Write メソッドを呼び出して、文字列を圧縮メモリストリームに書き込むだけです。書き込みが完了したら、Flush メソッドも呼び出して、キャッシュされている入力をすべて書き出します。

最後に、ToArray メソッドを使用して、メモリストリームによって作成されたバイト配列を返します。

手順4:文字列を展開するコードを追加します。

文字列を展開するには、圧縮時に実行した手順を逆に実行する必要があります。[文字列の圧縮解除]ボタンをダブルクリックし、btnExpandString_Click イベントを処理する次のコードを追加します。

コードのコピー
Private Sub btnExpandString_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExpandString.Click
    ' 文字列を展開します。
    Dim ticks As Long = DateTime.Now.Ticks
    TextBox1.Text = ExpandString(m_CompressedString)
    ' 処理にかかる時間をユーザーに通知します。
    Dim ms As Integer = (DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond
    Dim lenBefore As Integer = m_CompressedString.Length
    Dim lenAfter As Integer = TextBox1.Text.Length * 2
    Dim msg As String
    msg = String.Format("Expanded from {0} bytes to {1} bytes " & "in {2} milliseconds.", lenBefore, lenAfter, ms)
    MessageBox.Show(msg, "Expanded", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
コードのコピー
  private void btnExpandString_Click(object sender, RoutedEventArgs e)
        {
            // 文字列を展開します。
            long ticks = DateTime.Now.Ticks;
            string text = ExpandString(_compressedString);
            richTextBox1.Selection.Text = text;
            // 処理にかかる時間をユーザーに通知します。
            int ms = (int)((DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond);
            int lenBefore = _compressedString.Length;
            int lenAfter = text.Length * 2;
            string msg;
            msg = string.Format("Expanded from {0} bytes to {1} bytes " + "in {2} milliseconds.", lenBefore, lenAfter, ms);
            MessageBox.Show(msg, "Expanded", MessageBoxButton.OK);
        }

重要な行は、バイト配列を受け取って元の文字列を返すユーティリティ関数 ExpandString の呼び出しです。次のように ExpandString 関数のコードを追加します。

コードのコピー
Public Function ExpandString(ByVal buffer As Byte()) As String
    ' バッファをメモリストリームにします。
    Dim ms As MemoryStream = New MemoryStream(buffer)
    ' 圧縮解除プログラムストリームをメモリストリームにアタッチします。
    Dim sr As C1ZStreamReader = New C1ZStreamReader(ms)
    ' 圧縮解除されたデータを読み取ります。
    Dim reader As StreamReader = New StreamReader(sr)
    ExpandString = reader.ReadToEnd()
End Function
コードのコピー
public string ExpandString(byte[] buffer)
{
    // バッファをメモリストリームにします。
    MemoryStream ms = new MemoryStream(buffer);
    // 圧縮解除プログラムストリームをメモリストリームにアタッチします。
    C1ZStreamReader sr = new C1ZStreamReader(ms);
    // 圧縮解除されたデータを読み取ります。
    StreamReader reader = new StreamReader(sr);
    return reader.ReadToEnd();
}

ここでプロジェクトを実行すると、文字列を圧縮/圧縮解除してみることができます。テキストボックス内のテキストを変更するか、テキストボックスに新しい内容を貼り付け、文字列を圧縮/展開して、どの程度圧縮されるかを確認できます。

手順5:バイナリデータを圧縮するコードを追加します。

文字列の圧縮と同様に、バイナリデータも簡単に圧縮できます。唯一の違いは、圧縮プログラムストリームに StreamWriter オブジェクトをアタッチする代わりに、BinaryWriter オブジェクトをアタッチするという点です。

[データの圧縮]ボタンをダブルクリックし、btnCompressData_Click イベントを処理する次のコードを追加します。

コードのコピー
Private m_CompressedData As Byte()
Private Sub btnCompressData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCompressData.Click
    ' メモリストリームを開きます。
    Dim ms As MemoryStream = New MemoryStream()
    ' 圧縮プログラムストリームをメモリストリームにアタッチします。
    Dim sw As C1ZStreamWriter = New C1ZStreamWriter(ms)
    ' 圧縮プログラムストリームに BinaryWriter をアタッチします。
    Dim bw As BinaryWriter = New BinaryWriter(sw)
    ' 一連の数値をストリームに書き込みます。
    Dim i As Integer
    Dim count As Integer = 1000
    bw.Write(count)
    For i = 0 To count - 1
        Dim a As Double = i * Math.PI / 180.0
        bw.Write(i)
        bw.Write(a)
        bw.Write(Math.Sin(a))
        bw.Write(Math.Cos(a))
    Next i
    ' 保留中の出力をフラッシュします。
    bw.Flush()
    ' 圧縮データを保存します。
    m_CompressedData = ms.ToArray()
    ' 完了。
    Dim msg As String
    msg =String.Format("Generated table with {0} points," & " saved into {1} bytes", count, m_CompressedData.Length)
    Label1.Text = msg
    ' これで、展開することができます。
    btnExpandData.Enabled = True
End Sub
コードのコピー
private byte[] _compressedData;
private void btnCompressData_Click(object sender, RoutedEventArgs e)
        {
            // メモリストリームを開きます。
            MemoryStream ms = new MemoryStream();
            // 圧縮プログラムストリームをメモリストリームにアタッチします。
            C1ZStreamWriter sw = new C1ZStreamWriter(ms);
            // 圧縮プログラムストリームに BinaryWriter をアタッチします。
            BinaryWriter bw = new BinaryWriter(sw);
            // 一連の数値をストリームに書き込みます。
            int i;
            int count = 1000;
            bw.Write(count);
            for (i = 0; i <= count - 1; i++)
            {
                double a = i * Math.PI / 180.0;
                bw.Write(i);
                bw.Write(a);
                bw.Write(Math.Sin(a));
                bw.Write(Math.Cos(a));
            }
            // 保留中の出力をフラッシュします。
            bw.Flush();
            // 圧縮データを保存します。
            _compressedData = ms.ToArray();
            // 完了。
            string msg;
            msg = string.Format("Generated table with {0} points," +
saved into {1} bytes, count, _compressedData.Length);
            label1.Content = msg;
            // これで、展開することができます。
            btnExpandData.IsEnabled = true;
        }

このコードは、最初に m_CompressedData というメンバ変数を宣言します。これは、圧縮データ(バイト配列としてエンコードされる)を保持するために使用されます。

MemoryStreamC1ZStreamWriterBinaryWriter の各オブジェクトを前と同様に設定します。ただし、ここでは、StreamWriter の代わりに BinaryWriter を使用します。

次に、Write メソッドを使用して、データをストリームに書き込みます。BinaryWriter オブジェクトは、すべての基本オブジェクト型をストリームに書き込むことができるように、このメソッドをオーバーロードしています。最後に、前と同様に Flush メソッドを使用して、キャッシュされているデータがあればすべて圧縮ストリームに書き出します。

手順6:バイナリデータを展開するコードを追加します。

圧縮されたバイナリデータを展開するには、通常のストリームと同様に、圧縮解除プログラムストリームを設定し、データを読み取ります。

[データの圧縮解除]コマンドボタンに次の Click イベントハンドラコードを追加します。

コードのコピー
Private Sub btnExpandData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExpandData.Click
    ' 保存されたデータのメモリストリームを開きます。
    Dim ms As MemoryStream = New MemoryStream(m_CompressedData)
    ' 圧縮解除プログラムストリームをメモリストリームにアタッチします。
    Dim sr As C1ZStreamReader = New C1ZStreamReader(ms)
    ' 圧縮解除されたデータを読み取ります。
    Dim i As Integer
    Dim br As BinaryReader = New BinaryReader(sr)
    Dim count As Integer = br.ReadInt32()
    For i = 0 To count - 1
        Dim deg As Integer = br.ReadInt32()
        Dim rad As Double = br.ReadDouble()
        Dim sin As Double = br.ReadDouble()
        Dim cos As Double = br.ReadDouble()
    Next i
    ' 処理が完了したことをユーザーに通知します。
    Dim msg As String
    msg = String.Format("Read table with {0} points " & "from stream with {1} bytes.", count, m_CompressedData.Length)
    Label1.Text = msg
End Sub
コードのコピー
private void btnExpandData_Click(object sender, RoutedEventArgs e)
{
    // 保存されたデータのメモリストリームを開きます。
    MemoryStream ms = new MemoryStream(_compressedData);
    // 圧縮解除プログラムストリームをメモリストリームにアタッチします。
    C1ZStreamReader sr = new C1ZStreamReader(ms);
    // 圧縮解除されたデータを読み取ります。
   int i;
    BinaryReader br = new BinaryReader(sr);
    int count = br.ReadInt32();
    for (i = 0 ; i <= count - 1; i++)
    {
        int deg = br.ReadInt32();
        double rad = br.ReadDouble();
        double sin = br.ReadDouble();
        double cos = br.ReadDouble();
    }
    // 処理が完了したことをユーザーに通知します。
   string  msg;
    msg = string .Format("Read table with {0} points " +
        "from stream with {1} bytes.", count, _compressedData.Length);
    label1.Content = msg;
}

このコードでは、データは読み取られますが、画面には表示されません。コードをデバッグモードでステップ実行すると、読み取られたデータが書き込まれたデータと同じであることを確認できます。

プロジェクトを実行し、[データの圧縮]/[データの圧縮解除]ボタンをクリックすると、データが 14,125 バイトの配列に保存されることがわかります。このデータを通常のストリームに保存する場合は、28,004 バイト(4 + 1000 * (4 + 8 * 3))が必要です。このため、元のサイズの約半分の大きさに圧縮されたことになります。

これで「メモリでのデータの圧縮」チュートリアルは終了です。