確定的なGUIDを作成する方法


103

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

1つの明白な解決策は、ファイル名とそれらに使用されるGUIDを使用して静的辞書を作成することでした。次に、ファイルを生成するときはいつでも、ファイル名の辞書を検索し、対応するGUIDを使用します。しかし、これは、数百のファイルにスケーリングする可能性があり、GUIDの大きなリストを維持する必要がないため、実行できません。

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

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は常に同じになります。

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

回答:


151

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

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

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

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


5
@Porges:RFC4122は正しくなく、Cコードを修正するエラッタがあります(rfc-editor.org/errata_search.php?rfc=4122&eid=1352)。この実装がRFC4122とそのエラッタに完全に準拠していない場合は、詳細を提供してください。基準にしたいです。
Bradley Grainger 2013

1
@BradleyGrainger:気づかなかった、ありがとう/ごめんなさい!RFCを読むときは、常にエラッタを確認することを忘れないでください... :)
ポルジェス2013

3
@Porges:問題ありません。それは彼らが正誤表からの訂正でRFCをその場で更新しないという心を揺さぶる。ドキュメントの最後にあるリンクでさえ、読者にエラータの検索を忘れないようにすることよりもはるかに役立つでしょう(できればRFCに基づく実装を書く前に ...)。
Bradley Grainger

1
@BradleyGrainger:HTMLバージョンを使用している場合、ヘッダーからエラッタへのリンクがあります(例:tools.ietf.org/html/rfc4122)。常にHTMLバージョンにリダイレクトするブラウザ拡張機能があるのか​​どうか疑問に思います...
porges

2
:あなたはここにある.NET .NETレポにこれを貢献する検討すべきであるgithub.com/dotnet/coreclr/tree/master/src/mscorlib/src/System
sapphiremirage

29

これにより、外部のアセンブリをインポートすることなく、任意の文字列が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データキーに一貫してアップグレードする方法です。


このスニペットは、連合配布用のデータベースで一意の識別子を使用する場合に役立ちます。
Gleno

6
警告!このコードは、有効なGUID / UUIDを生成しません(以下のbacarも同様)。バージョンもタイプフィールドも正しく設定されていません。
MarkusSchaber 2016年

3
MD5はすでに16バイトの長さなので、SHA1の代わりにMD5CryptoServiceProviderを使用するのと同じくらい効果的ではないでしょうか?
Brain2000

20

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

RFC 4122バージョン3,5使用MD5及びSHA1(それぞれ) -のUUIDには、具体的には、決定論的(名前ベース)のUUIDを可能にします。ほとんどの人はおそらくランダムなバージョン4に慣れているでしょう。ウィキペディアにバージョンの概要がわかります。(ここでの「バージョン」という単語の使用は、UUIDの「タイプ」を表すようです-バージョン5はバージョン4に取って代わりません)。

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


1
これはまさにオリジナルのポスターが後にあるものです。UUIDには、文字列から始めてそれをGUIDに変換するアルゴリズムがすでにあります。UUIDバージョン3はMD5で文字列をハッシュし、バージョン5はSHA1で文字列をハッシュします。「GUID」を作成する際の重要な点は、それを他のGUIDに対して「一意」にすることです。でなければならないセット、ならびにニブルがそれのバージョン3または5の場合に応じて、3または5のいずれかに設定されているアルゴリズムが定義2ビット
イアン・ボイド

2
「バージョン」という言葉の使用に関して、RFC 4122§4.1.3は次のように述べています。
Bradley Grainger

11
GitHubにv3とv5のGUIDを作成するためのC#コードをいくつか投稿しました:github.com/LogosBible/Logos.Utility/blob/master/src/…–
Bradley Grainger

@BradleyGrainger、私は警告のビットごとのOR演算子が符号拡張されたオペランドで使用されます。最初に小さい符号なしの型にキャストすることを検討してください
セバスチャン

1
これは話題から外れています!個々のlibバグレポートをGitHubに移動することを提案します。
bacar

3

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

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

GUIDデータ型への純粋なGUIDでないものをシューホーンしようとすると、将来的にメンテナンスの問題が発生する可能性があります...


2
「ハッシュは衝突の可能性がはるかに高い... Guid.NewGuidで作成されたGuidよりも高い。」詳しく説明していただけますか?数学的な観点から見ると、設定できるビット数は同じであり、MD5とSHA1はどちらも暗号化ハッシュであり、(偶発的および意図的な)ハッシュ衝突の確率を下げるように特別に設計されています。
MarkusSchaber 2016年

主な違いは、関数を使用して暗号ハッシュが1つの無限空間から別の固定空間にマッピングされることです。可変長文字列を128ビットにマップするハッシュをイメージングするのに対して、Guidは疑似ランダム128ビットを生成します。疑似ランダム生成は、初期入力に依存せず、ハードウェアまたは他の手段からシードされたランダム性を使用して、出力空間で均一に出力を生成します。
Thai Bui、

2

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

ところで、個人的な意見ですが、md5ハッシュをGUIDとしてドレスアップしても、それは良いGUIDにはなりません。GUIDはその性質上、非決定的です。これはチートのように感じます。なぜスペードをスペードと呼び、その文字列でレンダリングされた入力のハッシュを言うのではないのでしょうか。新しいGUID行ではなく、次の行を使用してそれを行うことができます。

string stringHash = BitConverter.ToString(hashBytes)

入力ありがとうございます。これでも文字列が
返され

さて、あなたのハッシュを「GUID」と呼んでください、問題は解決しました。または、オブジェクトが必要なという本当の問題Guidですか?
user7116

私はそれがそんなに単純だったらいいのに.. :)しかし、はい、「GUID」オブジェクトが必要です
Punit Vora

5
「その性質上、GUIDは非決定的です」-これはGUIDの特定のタイプ(「バージョン」)にのみ当てはまります。ただし、@ Bradley Graingerと@Rob Fonseca-Ensorで述べられている他の理由、およびこの質問に対する私の回答では、「md5ハッシュをGUIDとしてドレスアップしても適切なGUIDにはならない」ことに同意します。
11
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.