118

在我们的应用程序中,我们正在创建具有 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 总是相同的。

有没有其他方法或推荐的方法来做到这一点?这种方法的优点或缺点是什么?

4

6 回答 6

164

正如@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)。

于 2011-04-14T01:15:30.180 回答
30

这会将任何字符串转换为 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 数据键的方法。

于 2012-02-21T22:14:38.863 回答
22

正如 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 的)

于 2010-06-11T14:09:18.817 回答
3

您需要区分 class 的实例Guid和全局唯一的标识符。“确定性 guid”实际上是一个散列(正如您对 的调用所证明的那样provider.ComputeHash)。与通过Guid.NewGuid.

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

尝试将不是纯 GUID 的东西硬塞到 GUID 数据类型中可能会导致将来出现维护问题......

于 2010-04-19T15:35:02.733 回答
1

MD5比较弱,相信你可以用SHA-1做同样的事情,得到更好的结果。

顺便说一句,只是个人意见,将 md5 散列装扮成 GUID 并不能使它成为一个好的 GUID。GUID 就其本质而言是非确定性的。这感觉像是作弊。为什么不直截了当地说它是一个字符串渲染的输入哈希。你可以通过使用这条线来做到这一点,而不是新的指导线:

string stringHash = BitConverter.ToString(hashBytes)
于 2010-04-15T02:28:20.240 回答
0

这是一个非常简单的解决方案,对于单元/集成测试之类的事情应该足够好:

var rnd = new Random(1234); // Seeded random number (deterministic).
Console.WriteLine($"{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}-{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}-{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}-{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}-{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}{rnd.Next(0, 255):x2}");
于 2021-07-27T12:45:00.433 回答