2

私は現在、過度のメモリプレッシャーを引き起こすファイルをハッシュする必要があるという問題に直面しており、ファイルストリームを使用してその場でハッシュを作成できるかどうかを調べようとしています。

可能性を調査している間、私は簡単な小さなテストを作成し、MD5のComputeHashが文字列とストリームを受け取るメソッド呼び出し間で同じハッシュを返すことを確認することにしました。

let CreateMD5HashFromString (value: string) =
     Convert.ToBase64String(MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(value)))

let CreateMD5HashFromStream (value: Stream) =
     Convert.ToBase64String(MD5.Create().ComputeHash(value))

次の単体テストで通話をテストしています。

[<TestMethod>]
member this.``CreateMD5Hash is the same between a string and a file stream`` () =
    let sampleText = File.ReadAllText("Sample.txt")
    let textMD5 = Security.CreateMD5HashFromString(sampleText);
    let streamMD5 = Security.CreateMD5HashFromStream(File.OpenRead("Sample.txt"))

    Assert.AreEqual(textMD5, streamMD5)

テスト用の小さなサンプルファイルを読み取っています。生成されたハッシュが異なるため、このテストは失敗します。私にはこれは正しくないように見えますが、正確にはわかりません。これらが同じであるべきかどうか誰かが確かに知っていますか?

また、2番目の質問ですが、ComputeHashのストリーム過負荷を使用してメモリの問題を節約していますか、それともハッシュする前にストリーム全体をロードしますか?関連する.NETアセンブリを分解しようとしましたが、HashCoreが内部で何をしているのかを追跡しようとして迷子になりました。

4

2 に答える 2

4

実際には非常に単純です。テキストがその基になるバイナリ表現と等しいと仮定することはできません。

サンプル テキストをASCIIとして作成および読み取るこのサンプルでは、​​期待どおりに正常に動作します。

public static void Main(string[] args)
{
    System.IO.File.WriteAllBytes("test", System.Text.Encoding.ASCII.GetBytes("test string"));

    var inputString = System.IO.File.ReadAllText("test");
    var inputBytes = System.IO.File.ReadAllBytes("test");
    var inputStream = new System.IO.FileStream("test", System.IO.FileMode.OpenOrCreate);

    var stringHash = Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(System.Text.Encoding.ASCII.GetBytes(inputString)));
    var streamHash = Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(inputStream));
    var bytesHash = Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(inputBytes));

    Console.WriteLine("String hash: {0}", stringHash);
    Console.WriteLine("Stream hash: {0}", streamHash);
    Console.WriteLine("Bytes hash: {0}", streamHash);

    Console.WriteLine("\nMD5s {0}", stringHash == streamHash && streamHash == bytesHash ? "match" : "don't match");
}

出力あり

String hash: b421md6Yb6t6IWJbeRZYnA==
Stream hash: b421md6Yb6t6IWJbeRZYnA==
Bytes hash: b421md6Yb6t6IWJbeRZYnA==

MD5s match

ただし、これはディスク上のファイルがプレーン ASCII であると仮定してのみ機能します。それ以外の場合はゼロ保証です。たとえば、多くの非 ASCII ファイルは、エンコードの種類を示す BOM (バイト オーダー マーカー) で始まります。これはバイナリ バイト配列ハッシュで表されますが、メモリ内の文字列ハッシュでは表されません。一般に、UTF-8 と Unicode は、同じ文字列に対して数十の異なる表現を持つことができます。文字列は、stringオブジェクトにロードされるときに、ディスク上のものとは異なる表現に正規化できます。

于 2013-01-08T00:59:44.927 に答える
2

重要な質問は、ソース ファイルでどのエンコーディングが使用されているかということだと思います。

使用するバイト配列Encoding.ASCII.GetBytesに.StreamGetBytes

これは MD5 機能とは関係がないため、チェックすることでより簡単にテストできます (ファイルが 10kB 未満であると仮定します。それ以外の場合は、より大きなバッファーが必要です)。

let res1 = Encoding.ASCII.GetBytes(File.ReadAllText("test.txt"))
let buffer = Array.zeroCreate 10240
let size = File.OpenRead("D:\\temp\\test.fsx").Read(buf, 0, 10240)
let res2 = buffer.[0 .. size - 1]

res1 = res2 // Are the byte arrays the same?

これを実行しようとしたとき、次の 2 つのことを解決する必要がありました。

  • 私が使用したファイルは、署名付きの UTF-8 で保存されていたため、先頭に 3 バイトがあり、エンコードを指定していました (使用した場合、同じバイト配列しか得られませんでした)。buffer.[3 .. size - 1]

  • 同じエンコーディングでファイルを保存する必要がありました (この場合は ASCII ですが、これを正しく行うには一般的に注意が必要です)。または、ファイルを読み取るときにエンコーディングを指定できますが、意味のないテキストをハッシュしている可能性があります。

于 2013-01-08T01:00:18.307 に答える