如何创建确定性指南


103

在我们的应用程序中,我们正在创建具有属性Guid值的Xml文件。此值需要在文件升级之间保持一致。因此,即使文件中的其他所有内容均发生更改,该属性的guid值也应保持不变。

一种明显的解决方案是使用文件名和要使用的Guid创建一个静态字典。然后,无论何时生成文件,我们都会在字典中查找文件名并使用相应的guid。但这是不可行的,因为我们可能会扩展到100个文件,并且不想维护大量的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将始终相同。

还有其他方法或建议的方法吗?该方法的优缺点是什么?

Answers:


151

如@bacar所述,RFC 4122§4.3 定义了一种创建基于名称的UUID的方法。这样做的好处(仅使用MD5哈希)是保证它们不会与非基于名称的UUID冲突,并且与其他基于名称的UUID发生冲突的可能性非常小。

.NET Framework没有本地支持创建它们,但是我在GitHub上发布了实现该算法的代码。可以如下使用:

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

为了进一步降低与其他GUID发生冲突的风险,您可以创建一个专用GUID用作名称空间ID(而不是使用RFC中定义的URL名称空间ID)。


5
@Porges:RFC4122不正确,并且具有修正C代码的勘误表(rfc-editor.org/errata_search.php?rfc=4122&eid=1352)。如果此实现不完全符合RFC4122及其勘误表,请提供更多详细信息;我想使其遵循标准。
Bradley Grainger

1
@BradleyGrainger:谢谢/抱歉,我没有注意到!我应该始终记得在阅读RFC ...时检查勘误表... :)
porges

3
@Porges:不客气/没问题。令他们感到困惑的是,他们没有使用勘误表中的更正来就地更新RFC。即使是文档末尾的链接,也比依靠读者记住搜索勘误要大得多(希望编写基于RFC的实现之前 ……)。
Bradley Grainger

1
@BradleyGrainger:如果使用的是HTML版本,则它具有从标题到勘误的链接,例如tools.ietf.org/html/rfc4122。我想知道是否有一个浏览器扩展程序始终重定向到HTML版本...
porges 2013年

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 '16

3
因为MD5已经是16字节长,所以使用MD5CryptoServiceProvider代替SHA1会一样有效吗?
Brain2000 '16

20

正如Rob提到的那样,您的方法不会生成UUID,而是会生成看起来像UUID的哈希。

关于UUID 的RFC 4122特别允许确定性(基于名称)UUID-版本3和5分别使用md5和SHA1。大多数人可能都熟悉版本4,这是随机的。维基百科对版本进行了很好的概述。(请注意,此处使用的“版本”一词似乎描述了UUID的“类型”-版本5不会取代版本4)。

似乎有一些库可以生成3/5版本的UUID,包括python uuid模块boost.uuid(C ++)和OSSP UUID。(我没有寻找任何.net的)


1
这正是原始海报的目的。UUID已经有一种算法,您可以从字符串开始并将其转换为GUID。UUID版本3使用MD5哈希字符串,而版本5使用SHA1哈希字符串。创建“ guid”的重点是使其与其他GUID“唯一”。该算法定义了两个比特必须被设置,以及一个半字节被设定为3或5,取决于它是否的版本3或5
伊恩博伊德

2
关于“版本”一词的使用,RFC 4122§4.1.3指出:“版本更准确地是子类型;同样,我们保留术语兼容性。”
Bradley Grainger

11
我张贴一些C#代码在GitHub上创建V3和V5的GUID:github.com/LogosBible/Logos.Utility/blob/master/src/...
布拉德利固安捷

@BradleyGrainger,我得到警告在符号扩展操作数上使用按位或运算符;考虑强制转换为更小的无符号类型第一
塞巴斯蒂安

1
这越来越离题了!建议将单个库错误报告移至GitHub。
bacar 2012年

3

您需要在类的实例Guid和全局唯一的标识符之间进行区分。“确定性指南”实际上是一个哈希(如您对的调用所证明provider.ComputeHash)。与通过创建的Guid相比,哈希具有更高的发生冲突的机会(两个不同的字符串碰巧产生相同的哈希)Guid.NewGuid

因此,您的方法存在的问题是,两条不同的路径将产生相同的GUID的可能性将是必须的。如果您需要一个对于任何给定路径字符串而言唯一的标识符,那么最简单的方法就是使用string。如果您需要用户屏蔽字符串,请对其进行加密 -您可以使用ROT13或更强大的功能...

尝试将不是纯GUID的内容塞入GUID数据类型中可能会在将来导致维护问题...


2
您声称“哈希的碰撞几率要比通过Guid.NewGuid创建的Guid高得多。” 您能详细说明一下吗?从数学的角度来看,可以设置的位数是相同的,并且MD5和SHA1都是加密哈希,专门设计用于降低(偶然和故意)哈希冲突的可能性。
MarkusSchaber '16

我想说的主要区别是使用函数从一个无限空间到另一个固定空间的加密哈希映射。映像一个将可变长度字符串映射到128位的哈希,而Guid生成伪随机的128位。伪随机生成不依赖于初始输入,而是通过使用从硬件或其他方式产生的随机性在输出空间中均匀生成输出来实现。
泰国北碧

2

MD5较弱,我相信您可以使用SHA-1做同样的事情并获得更好的结果。

顺便说一句,只是个人观点,将md5哈希表打扮成GUID并不能使其成为一个好的GUID。GUID本质上是不确定的。感觉像是在作弊。为什么不只是将锹称为锹,而只是说它的字符串是输入的哈希值。您可以使用以下行而不是新的guid行:

string stringHash = BitConverter.ToString(hashBytes)

感谢您的输入,但这仍然给我一个字符串,我正在寻找GUID ...
Punit Vora 2010年

好的,将您的哈希称为“ GUID”,问题已解决。还是真正的问题是您需要一个Guid物体?
user7116'4

我希望它是如此简单.. :)但是,我需要一个“ GUID”对象
Punit Vora 2010年

5
“ GUID就其本质而言是不确定的”-这仅适用于某些类型的GUID(“版本”)。但是,我同意@Bradley Grainger和@Rob Fonseca-Ensor阐明的其他原因“将md5散列为GUID并不能带来良好的GUID”,以及我对此问题的回答。
巴卡尔2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.