在我们的应用程序中,我们使用具有Guid值的属性创建Xml文件。该值必须在文件升级之间保持一致。因此,即使文件中的其他内容发生更改,该属性的guid值也应保持不变。
一个明显的解决方案是创建一个静态字典,其中包含文件名和用于它们的Guids。然后每当我们生成文件时,我们都会在字典中查找文件名并使用相应的guid。但这是不可行的,因为我们可能会扩展到100个文件,并且不想保留大量的guid。
所以另一种方法是根据文件的路径使Guid相同。由于我们的文件路径和应用程序目录结构是唯一的,因此Guid对于该路径应该是唯一的。因此,每次我们运行升级时,文件都会根据其路径获得相同的guid。我找到了一种很酷的方式来生成这样的'Deterministic Guids'(感谢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将始终是相同的。
有没有其他方法或建议的方法来做到这一点?该方法的优点或缺点是什么?
答案 0 :(得分:137)
如@bacar所述,RFC 4122§4.3定义了一种创建基于名称的UUID的方法。这样做(仅使用MD5哈希)的优点是保证不会与非基于命名的UUID冲突,并且与其他基于名称的UUID碰撞的可能性非常小(非常小)。
在.NET Framework中没有用于创建这些的本机支持,但我发布了实现该算法的code on GitHub。它可以使用如下:
Guid guid = GuidUtility.Create(GuidUtility.UrlNamespace, filePath);
为了进一步降低与其他GUID冲突的风险,您可以创建一个私有GUID作为命名空间ID(而不是使用RFC中定义的URL命名空间ID)。
答案 1 :(得分:28)
这会将任何字符串转换为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数据密钥的方法。
答案 2 :(得分:18)
正如Rob提到的那样,你的方法不会生成UUID,它会生成一个看起来像UUID的哈希。
UUID上的RFC 4122专门允许确定性(基于名称)的UUID - 版本3和5分别使用md5和SHA1。大多数人可能熟悉版本4,这是随机的。 Wikipedia可以很好地概述版本。 (请注意,在这里使用“版本”一词似乎描述了UUID的“类型” - 版本5并未取代版本4)。
似乎有一些库用于生成版本3/5 UUID,包括python uuid module,boost.uuid(C ++)和OSSP UUID。 (我没有找过任何.net的)
答案 3 :(得分:5)
MD5很弱,我相信你可以用SHA-1做同样的事情并获得更好的结果。
BTW,只是个人意见,将md5哈希作为GUID打扮并不能使它成为一个好的GUID。 GUID本质上是非确定性的。这感觉就像是作弊。为什么不直接将spade称为spade,只是说它是一个字符串渲染输入的哈希值。你可以通过使用这一行,而不是新的guid行来做到这一点:string stringHash = BitConverter.ToString(hashBytes)
答案 4 :(得分:3)
您需要区分类Guid
的实例和全局唯一的标识符。 “确定性guid”实际上是一个哈希(通过调用provider.ComputeHash
来证明)。哈希比通过Guid.NewGuid
创建的Guid具有更高的碰撞机会(发生两个不同的字符串以产生相同的哈希)。
因此,您的方法存在的问题是您必须确定两条不同路径可能会生成相同的GUID。如果您需要一个对任何给定路径字符串唯一的标识符,那么最简单的方法是只使用字符串。如果您需要从用户隐藏字符串,加密 - 您可以使用ROT13或更强大的功能......
尝试将GUID数据类型中不是纯GUID的东西偷偷摸摸可能导致将来出现维护问题......
答案 5 :(得分: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}");