web-dev-qa-db-ja.com

決定論的ガイドを作成する方法

このアプリケーションでは、Guid値を持つ属性を持つXmlファイルを作成しています。この値は、ファイルのアップグレード間で一貫している必要がありました。そのため、ファイル内の他のすべてが変更された場合でも、属性のGUID値は同じままである必要があります。

明らかな解決策の1つは、ファイル名とそれらに使用するGUIDを使用して静的辞書を作成することでした。次に、ファイルを生成するたびに、ファイル名の辞書を検索し、対応するGUIDを使用します。しかし、これは現実的ではありません。何百ものファイルに拡張でき、大きなGUIDのリストを維持したくないからです。

別のアプローチは、ファイルのパスに基づいてGUIDを同じにすることでした。ファイルパスとアプリケーションディレクトリ構造は一意であるため、Guidはそのパスに対して一意である必要があります。そのため、アップグレードを実行するたびに、ファイルはそのパスに基づいて同じGUIDを取得します。そのような ' 決定論的ガイド '(エルトン・ストーンマンに感謝)を生成するクールな方法を見つけました。基本的にこれを行います:

private Guid GetDeterministicGuid(string input) 

{ 

//use MD5 hash to get a 16-byte hash of the string: 

MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); 

byte[] inputBytes = Encoding.Default.GetBytes(input); 

byte[] hashBytes = provider.ComputeHash(inputBytes); 

//generate a guid from the hash: 

Guid hashGuid = new Guid(hashBytes); 

return hashGuid; 

} 

したがって、文字列を指定すると、Guidは常に同じになります。

これを行う他のアプローチや推奨される方法はありますか?その方法の長所と短所は何ですか?

94
Punit Vora

@bacarで述べたように、 RFC 4122 §4.3は、名前ベースのUUIDを作成する方法を定義しています。 (MD5ハッシュを使用するだけで)これを行う利点は、これらが非名前ベースのUUIDと衝突しないことが保証され、他の名前ベースのUUIDと衝突する可能性が非常に(非常に)少ないことです。

これらを作成するための.NET Frameworkのネイティブサポートはありませんが、アルゴリズムを実装する GitHubのコード を投稿しました。次のように使用できます。

Guid guid = GuidUtility.Create(GuidUtility.UrlNamespace, filePath);

他のGUIDとの衝突のリスクをさらに低減するために、プライベートGUIDを作成して、RFCで定義されたURL名前空間IDを使用する代わりに)を名前空間IDとして使用できます。

140

これにより、外部のアセンブリをインポートすることなく、文字列がGUIDに変換されます。

public static Guid ToGuid(string src)
{
    byte[] stringbytes = Encoding.UTF8.GetBytes(src);
    byte[] hashedBytes = new System.Security.Cryptography
        .SHA1CryptoServiceProvider()
        .ComputeHash(stringbytes);
    Array.Resize(ref hashedBytes, 16);
    return new Guid(hashedBytes);
}

一意のGuidを生成する方法ははるかに優れていますが、これは文字列データキーをGuidデータキーに一貫してアップグレードする方法です。

28
Ben Gripka

Robが言及しているように、メソッドはUUIDを生成せず、UUIDのようなハッシュを生成します。

UUIDの RFC 4122 は、明確な(名前ベースの)UUIDを明確に許可します-バージョン3と5は、それぞれmd5とSHA1を使用します。ほとんどの人は、おそらくバージョン4に精通しているでしょう。これはランダムです。 Wikipedia は、バージョンの概要を提供します。 (ここでの「バージョン」という単語の使用は、UUIDの「タイプ」を表すように思われることに注意してください-バージョン5はバージョン4に優先しません)。

python uuid moduleboost.uuid (C++)、および OSSP UUIDを含む、バージョン3/5 UUIDを生成するためのライブラリがいくつかあるようです 。 (.netのものを探していません)

18
bacar

MD5は弱く、SHA-1でも同じことができ、より良い結果が得られると思います。

ところで、個人的な意見であり、md5ハッシュをGUIDとしてドレスアップしても良いGUIDにはなりません。GUIDは本質的に決定的ではありません。スペードをスペードと呼び、その文字列が入力のハッシュをレンダリングしたと言うだけです。

string stringHash = BitConverter.ToString(hashBytes)
5
ryber

クラスGuidのインスタンスと、グローバルに一意な識別子を区別する必要があります。 「決定論的GUID」は実際にはハッシュです(provider.ComputeHashへの呼び出しによって証明されるように)。ハッシュは、Guid.NewGuidを介して作成されたGUIDよりも、衝突(2つの異なる文字列が同じハッシュを生成する)の可能性がはるかに高くなります。

そのため、このアプローチの問題は、2つの異なるパスが同じGUIDを生成する可能性があることで大丈夫でなければならないことです。特定のパス文字列に対して一意の識別子が必要な場合、最も簡単なのはstringを使用することです。ユーザーから文字列を隠す必要がある場合は、暗号化-ROT13またはより強力なものを使用できます...

純粋なGUID以外の何かをGUIDデータ型に挿入しようとすると、将来的にメンテナンスの問題が発生する可能性があります...

3