.NET TIPS

進行状況を表示しながらハッシュ値を計算するには?[C#、VB]

デジタルアドバンテージ 一色 政彦
2010/01/21

 「TIPS:SHA-1/SHA-2/MD5ハッシュ値を計算するには?」ではハッシュ値(=データを元に生成した固定長の一意な値)を計算する方法を説明した。その方法とは、アルゴリズム・クラスのComputeHashメソッドを呼び出すというものだった。

 しかし、この方法で大容量のデータ(例えば動画ファイルなど)のハッシュ値を計算すると、メソッドが完了するまでに時間がかかる場合がある。このため、UI(ユーザー・インターフェイス)を持つアプリケーションでは表示が更新されずに、エンド・ユーザーはアプリケーションが固まったように感じるかもしれない。これを回避する1つの手段は、計算途中の進行状況をエンド・ユーザーに表示することである。

 そこで本稿では、進行状況を表示しながらハッシュ値を計算する方法を紹介する。

●進行状況を表示しながらハッシュ値を計算する方法

 その方法とは、ComputeHashメソッドの代わりに、TransformBlockメソッドおよびTransformFinalBlockメソッドを利用することである。

 これらのメソッドは、大きなデータを小分けにしてハッシュ値を計算するためのものである。TransformBlockメソッドで先頭から小量ずつデータを処理していき、最後のデータの処理でTransformFinalBlockメソッドを呼び出すことで、最終的なハッシュ値が計算できる。

 つまり、TransformBlockメソッドとTransformFinalBlockメソッドを呼び出す合間合間で、進ちょく状況を計算して、ユーザーに表示すればよいわけだ。

 それぞれのメソッドの構文は以下のようになっている。

 TransformBlockメソッド

  • 第1パラメータ(Byte[]):入力データ(=大容量データ全体のバイト配列)。
  • 第2パラメータ(Int32):入力データの開始オフセット(=大容量データのうち、小量データとして使用する開始位置)。
  • 第3パラメータ(Int32):入力データで処理するバイト数(=小量データとして使用するバイト配列の数)。
  • 第4パラメータ(Byte[]):出力データ(=今回処理された少量データを受け取る出力先のバイト配列)。
  • 第5パラメータ(Int32):出力データの開始オフセット(=出力先のバイト配列内の書き込み開始位置)。
  • 戻り値(Int32):処理したバイト数。

 TransformFinalBlockメソッド

  • 第1パラメータ(Byte[]):入力データ(=大容量データ全体のバイト配列)。
  • 第2パラメータ(Int32):入力データの開始オフセット(=大容量データのうち、小量データとして使用する開始位置)。
  • 第3パラメータ(Int32):入力データで処理するバイト数(=小量データとして使用するバイト配列の数)。
  • 戻り値(System.Byte[]):出力データ(=今回処理されたデータのバイト配列)。

 いずれのメソッドも戻り値はエラー・チェックぐらいにしか使い道はないだろう。本稿では戻り値は無視する。

 また、TransformBlockメソッドの第4パラメータでは処理済みデータのバイト配列を受け取れるが、これも使い道があまりない。出力データが不要な場合は、第4パラメータにnull/Nothingを、第5パラメータには0を指定するとよい。これにより、出力データのコピー処理が発生しないので、若干処理速度が速まるはずだ。

 それでは、この2つのメソッドを使ったサンプル・コードを示す。

 次のコードは、大容量の動画ファイル・データを1024バイトずつ先頭から順に処理していき、その間、0〜100%の1%ずつ進行状況を表示する。最終的に、SHA256アルゴリズムにより動画ファイル全体のデータのハッシュ値を求め、その結果をコンソール出力している。なお、進行状況の表示方法については、「TIPS:コンソール・アプリケーションで進行状況を表示するには?」を参照してほしい。

using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;

class Program
{
  static void Main()
  {
    // 進行状況を表示するための設定や変数
    Console.CursorVisible = false;
    int percent = 0;

    // ファイルをUTF-8エンコードでバイト配列化
    string filePath =
      @"C:\Users\Public\Videos\Sample Videos\Wildlife.wmv";
    byte[] byteValue = File.ReadAllBytes(filePath);
 
    // SHA256のハッシュ値を取得する
    SHA256 crypto = new SHA256CryptoServiceProvider();
    int length = byteValue.Length;
    int blockSize = 1024;
    decimal doneSize = 0;
   
    for (int offset = 0; offset < length; offset += blockSize)
    {
      if (offset + blockSize < length)
      {
        crypto.TransformBlock(
          byteValue, offset, blockSize, null, 0);
        doneSize = offset + blockSize;
      }
      else
      {
        crypto.TransformFinalBlock(
          byteValue, offset, length - offset);
        doneSize = length;
      }
      if (percent != doneSize * 100 / length)
      {
        percent = (int)(doneSize * 100 / length);
        Console.Write("{0, 3:d0}%", percent);
        Console.SetCursorPosition(0, Console.CursorTop);
      }
    }

    byte[] hashValue = crypto.Hash;

    // バイト配列をUTF8エンコードで文字列化
    StringBuilder hashedText = new StringBuilder();
    for (int i = 0; i < hashValue.Length; i++)
    {
      hashedText.AppendFormat("{0:X2}", hashValue[i]);
    }
    Console.WriteLine("");
    Console.WriteLine("ハッシュ値:" + hashedText.ToString());
    Console.WriteLine("文字数:" + hashedText.Length.ToString());

    // 出力例(「100%」の部分の表示で処理進行の状況が示される):
    // 100%
   
// ハッシュ値:05AEFC9502534CAC696A25E5020AE40ABBC2A2BC918A9902A60673849A0857B4
    // 文字数:64

    // 出力を確認するために実行を停止
    Console.ReadLine();
  }
}
Imports System
Imports System.Security.Cryptography
Imports System.Text
Imports System.IO

Module Module1

  Sub Main()

    ' 進行状況を表示するための設定や変数
    Console.CursorVisible = False
    Dim percent As Integer = 0

    ' ファイルをUTF-8エンコードでバイト配列化
    Dim filePath = _
      "C:\Users\Public\Videos\Sample Videos\Wildlife.wmv"
    Dim byteValue As Byte() = File.ReadAllBytes(filePath)

    ' SHA256のハッシュ値を取得する
    Dim crypto As SHA256 = New SHA256CryptoServiceProvider()
    Dim length As Integer = byteValue.Length
    Dim blockSize As Integer = 1024
    Dim doneSize As Decimal = 0

    For offset As Integer = 0 To length - 1 Step blockSize

      If offset + blockSize < length Then
        crypto.TransformBlock( _
          byteValue, offset, blockSize, Nothing, 0)
        doneSize = offset + blockSize
      Else
        crypto.TransformFinalBlock( _
          byteValue, offset, length - offset)
        doneSize = length
      End If

      If percent <> doneSize * 100 / length Then
        percent = CType(doneSize * 100 / length, Integer)
        Console.Write("{0, 3:d0}%", percent)
        Console.SetCursorPosition(0, Console.CursorTop)
      End If

    Next

    Dim hashValue As Byte() = crypto.Hash

    ' バイト配列をUTF8エンコードで文字列化
    Dim hashedText As New StringBuilder()
    For i As Integer = 0 To hashValue.Length - 1
      hashedText.AppendFormat("{0:X2}", hashValue(i))
    Next

    Console.WriteLine("")
    Console.WriteLine("ハッシュ値:" & hashedText.ToString())
    Console.WriteLine("文字数:" & hashedText.Length.ToString())

    ' 出力例(「100%」の部分の表示で処理進行の状況が示される):
    ' 100%
    ' ハッシュ値:05AEFC9502534CAC696A25E5020AE40ABBC2A2BC918A9902A60673849A0857B4
    ' 文字数:64

    ' 出力を確認するために実行を停止
    Console.ReadKey()
  End Sub

End Module
進行状況を表示しながらSHA256アルゴリズムによりハッシュ値を計算するサンプル・コード(上:C#、下:VB)
本質とは関係のないコードを短くするために、.NET Framework 2.0以降でしか使えない(つまり.NET Framework 1.xで使えない)クラスやメソッド(例:File.ReadAllBytesメソッドなど)を利用しているので注意してほしい。

 上記のコードでは、SHA256CryptoServiceProviderクラスのハッシュ・アルゴリズムを利用しているが、.NETではこれ以外にもさまざまなハッシュ・アルゴリズムに対応している。詳しくは「TIPS:SHA-1/SHA-2/MD5ハッシュ値を計算するには?」を参照されたい。End of Article

カテゴリ:クラス・ライブラリ 処理対象:暗号
使用ライブラリ:MD5クラス(System.Security.Cryptography名前空間)
使用ライブラリ:SHA1クラス(System.Security.Cryptography名前空間)
使用ライブラリ:SHA256クラス(System.Security.Cryptography名前空間)
使用ライブラリ:SHA384クラス(System.Security.Cryptography名前空間)
使用ライブラリ:SHA512クラス(System.Security.Cryptography名前空間)
使用ライブラリ:RIPEMD160クラス(System.Security.Cryptography名前空間)
関連TIPS:SHA-1/SHA-2/MD5ハッシュ値を計算するには?
関連TIPS:コンソール・アプリケーションで進行状況を表示するには?

この記事と関連性の高い別の.NET TIPS
SHA-1/SHA-2/MD5ハッシュ値を計算するには?
時間がかかる処理の進行状況をダイアログで表示するには?
進行状況を表示しながらディレクトリやファイルをコピー/移動/削除するには?(My機能活用)
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間